mirror of
https://github.com/hemna/aprsd-slack-plugin.git
synced 2024-09-28 16:16:47 -04:00
Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
849639767d | |||
931771fc7b | |||
62e3532dd2 | |||
bc3595d7a2 | |||
e74585f4e7 | |||
8367cc8fd2 | |||
7c28bb9886 | |||
3bfc0188c0 | |||
63726e426e | |||
b624055d06 | |||
53d45e9e74 | |||
8cef8fbdbf | |||
c18823cf88 | |||
41b7c27a63 | |||
f6a456a586 | |||
0c63bc5fcf | |||
7fa7304423 | |||
d4010d7868 | |||
8bc5afe1ed | |||
3ce8471cb0 | |||
41468d5467 | |||
0a1d2b3e0e | |||
d983e1a824 | |||
bcc605dc2c | |||
d5d76a1a02 | |||
9b4f4b5a28 | |||
fc15ffd6eb | |||
488b83a157 | |||
812c9b3d43 | |||
36b2eff71c |
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: wb4bor
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
2
.github/workflows/python.yml
vendored
2
.github/workflows/python.yml
vendored
@ -7,7 +7,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.6, 3.7, 3.8, 3.9]
|
||||
python-version: [3.7, 3.8, 3.9]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -11,4 +11,4 @@ venv
|
||||
dist
|
||||
.pytest_cache
|
||||
.mypy_cache
|
||||
build
|
||||
build
|
||||
|
23
.pre-commit-config.yaml
Normal file
23
.pre-commit-config.yaml
Normal file
@ -0,0 +1,23 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v3.4.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
- id: detect-private-key
|
||||
- id: check-merge-conflict
|
||||
- id: check-case-conflict
|
||||
- id: check-docstring-first
|
||||
- id: check-builtin-literals
|
||||
|
||||
- repo: https://github.com/asottile/setup-cfg-fmt
|
||||
rev: v1.16.0
|
||||
hooks:
|
||||
- id: setup-cfg-fmt
|
||||
|
||||
- repo: https://github.com/dizballanze/gray
|
||||
rev: v0.10.1
|
||||
hooks:
|
||||
- id: gray
|
2
AUTHORS
Normal file
2
AUTHORS
Normal file
@ -0,0 +1,2 @@
|
||||
Hemna <waboring@hemna.com>
|
||||
Walter A. Boring IV <waboring@hemna.com>
|
44
ChangeLog
44
ChangeLog
@ -1,6 +1,50 @@
|
||||
CHANGES
|
||||
=======
|
||||
|
||||
v1.2.0
|
||||
------
|
||||
|
||||
* update
|
||||
* Update for aprsd 3.0.0
|
||||
|
||||
v1.1.0
|
||||
------
|
||||
|
||||
* Updated regex for message plugin
|
||||
* Removed import of aprsd.messaging
|
||||
* Fixed missing import for config
|
||||
* Update for APRSD 3.0.0 release
|
||||
* Update for aprsd >= 2.7.0
|
||||
|
||||
v1.0.5
|
||||
------
|
||||
|
||||
* 1.0.5
|
||||
* cleanup
|
||||
* fix tox runs
|
||||
* More cleanup
|
||||
* Create FUNDING.yml
|
||||
* update for new aprsd
|
||||
* update requirements.txt so it doesn't have so many
|
||||
* fixed build issues
|
||||
* Updated to work with 2.0.0
|
||||
* Added new notification and location plugin
|
||||
* Fixed to work with utils\_refactor
|
||||
* Update for new plugin interface. Add Notify plugin
|
||||
* Added geo description in location
|
||||
* fixed tests for tox
|
||||
* fixed tests after refactoring
|
||||
* prep for release
|
||||
* new updated location plugin
|
||||
* Updated slack formatting
|
||||
* Updates for v1.0.4 release
|
||||
* Added Makefile, pre-commit. Also update for 1.5.0
|
||||
|
||||
v1.0.3
|
||||
------
|
||||
|
||||
* Plugin gets the version from pbr version now
|
||||
|
||||
v1.0.2
|
||||
------
|
||||
|
||||
|
@ -173,4 +173,3 @@
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
|
73
Makefile
Normal file
73
Makefile
Normal file
@ -0,0 +1,73 @@
|
||||
REQUIREMENTS_TXT ?= requirements.txt requirements-dev.txt
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
.PHONY: help dev test
|
||||
include Makefile.venv
|
||||
Makefile.venv:
|
||||
curl \
|
||||
-o Makefile.fetched \
|
||||
-L "https://github.com/sio/Makefile.venv/raw/v2020.08.14/Makefile.venv"
|
||||
echo "5afbcf51a82f629cd65ff23185acde90ebe4dec889ef80bbdc12562fbd0b2611 *Makefile.fetched" \
|
||||
| sha256sum --check - \
|
||||
&& mv Makefile.fetched Makefile.venv
|
||||
|
||||
help: # Help for the Makefile
|
||||
@egrep -h '\s##\s' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
dev: venv ## Create the virtualenv with all the requirements installed
|
||||
|
||||
docs: build
|
||||
cp README.rst docs/readme.rst
|
||||
cp Changelog docs/changelog.rst
|
||||
tox -edocs
|
||||
|
||||
clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts
|
||||
|
||||
clean-build: ## remove build artifacts
|
||||
rm -fr build/
|
||||
rm -fr dist/
|
||||
rm -fr .eggs/
|
||||
find . -name '*.egg-info' -exec rm -fr {} +
|
||||
find . -name '*.egg' -exec rm -f {} +
|
||||
|
||||
clean-pyc: ## remove Python file artifacts
|
||||
find . -name '*.pyc' -exec rm -f {} +
|
||||
find . -name '*.pyo' -exec rm -f {} +
|
||||
find . -name '*~' -exec rm -f {} +
|
||||
find . -name '__pycache__' -exec rm -fr {} +
|
||||
|
||||
clean-test: ## remove test and coverage artifacts
|
||||
rm -fr .tox/
|
||||
rm -f .coverage
|
||||
rm -fr htmlcov/
|
||||
rm -fr .pytest_cache
|
||||
|
||||
coverage: ## check code coverage quickly with the default Python
|
||||
coverage run --source aprsd_repeat_plugins setup.py test
|
||||
coverage report -m
|
||||
coverage html
|
||||
$(BROWSER) htmlcov/index.html
|
||||
|
||||
test: dev ## Run all the tox tests
|
||||
tox -p all
|
||||
|
||||
build: test ## Make the build artifact prior to doing an upload
|
||||
$(VENV)/python3 setup.py sdist bdist_wheel
|
||||
$(VENV)/twine check dist/*
|
||||
|
||||
upload: build ## Upload a new version of the plugin
|
||||
$(VENV)/twine upload dist/*
|
||||
|
||||
check: dev ## Code format check with tox and pep8
|
||||
tox -epep8
|
||||
|
||||
fix: dev ## fixes code formatting with gray
|
||||
tox -efmt
|
||||
|
||||
update-requirements: dev ## Update the requirements.txt and dev-requirements.txt files
|
||||
rm requirements.txt
|
||||
rm requirements-dev.txt
|
||||
touch requirements.txt
|
||||
touch requirements-dev.txt
|
||||
$(VENV)/pip-compile --resolver backtracking --annotation-style line requirements.in
|
||||
$(VENV)/pip-compile --resolver backtracking --annotation-style line requirements-dev.in
|
183
Makefile.venv
Normal file
183
Makefile.venv
Normal file
@ -0,0 +1,183 @@
|
||||
#
|
||||
# SEAMLESSLY MANAGE PYTHON VIRTUAL ENVIRONMENT WITH A MAKEFILE
|
||||
#
|
||||
# https://github.com/sio/Makefile.venv v2020.08.14
|
||||
#
|
||||
#
|
||||
# Insert `include Makefile.venv` at the bottom of your Makefile to enable these
|
||||
# rules.
|
||||
#
|
||||
# When writing your Makefile use '$(VENV)/python' to refer to the Python
|
||||
# interpreter within virtual environment and '$(VENV)/executablename' for any
|
||||
# other executable in venv.
|
||||
#
|
||||
# This Makefile provides the following targets:
|
||||
# venv
|
||||
# Use this as a dependency for any target that requires virtual
|
||||
# environment to be created and configured
|
||||
# python, ipython
|
||||
# Use these to launch interactive Python shell within virtual environment
|
||||
# shell, bash, zsh
|
||||
# Launch interactive command line shell. "shell" target launches the
|
||||
# default shell Makefile executes its rules in (usually /bin/sh).
|
||||
# "bash" and "zsh" can be used to refer to the specific desired shell.
|
||||
# show-venv
|
||||
# Show versions of Python and pip, and the path to the virtual environment
|
||||
# clean-venv
|
||||
# Remove virtual environment
|
||||
# $(VENV)/executable_name
|
||||
# Install `executable_name` with pip. Only packages with names matching
|
||||
# the name of the corresponding executable are supported.
|
||||
# Use this as a lightweight mechanism for development dependencies
|
||||
# tracking. E.g. for one-off tools that are not required in every
|
||||
# developer's environment, therefore are not included into
|
||||
# requirements.txt or setup.py.
|
||||
# Note:
|
||||
# Rules using such target or dependency MUST be defined below
|
||||
# `include` directive to make use of correct $(VENV) value.
|
||||
# Example:
|
||||
# codestyle: $(VENV)/pyflakes
|
||||
# $(VENV)/pyflakes .
|
||||
# See `ipython` target below for another example.
|
||||
#
|
||||
# This Makefile can be configured via following variables:
|
||||
# PY
|
||||
# Command name for system Python interpreter. It is used only initialy to
|
||||
# create the virtual environment
|
||||
# Default: python3
|
||||
# REQUIREMENTS_TXT
|
||||
# Space separated list of paths to requirements.txt files.
|
||||
# Paths are resolved relative to current working directory.
|
||||
# Default: requirements.txt
|
||||
# WORKDIR
|
||||
# Parent directory for the virtual environment.
|
||||
# Default: current working directory.
|
||||
# VENVDIR
|
||||
# Python virtual environment directory.
|
||||
# Default: $(WORKDIR)/.venv
|
||||
#
|
||||
# This Makefile was written for GNU Make and may not work with other make
|
||||
# implementations.
|
||||
#
|
||||
#
|
||||
# Copyright (c) 2019-2020 Vitaly Potyarkin
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0
|
||||
# <http://www.apache.org/licenses/LICENSE-2.0>
|
||||
#
|
||||
|
||||
|
||||
#
|
||||
# Configuration variables
|
||||
#
|
||||
|
||||
PY?=python3
|
||||
WORKDIR?=.
|
||||
VENVDIR?=$(WORKDIR)/.venv
|
||||
REQUIREMENTS_TXT?=$(wildcard requirements.txt) # Multiple paths are supported (space separated)
|
||||
MARKER=.initialized-with-Makefile.venv
|
||||
|
||||
|
||||
#
|
||||
# Internal variable resolution
|
||||
#
|
||||
|
||||
VENV=$(VENVDIR)/bin
|
||||
EXE=
|
||||
# Detect windows
|
||||
ifeq (win32,$(shell $(PY) -c "import __future__, sys; print(sys.platform)"))
|
||||
VENV=$(VENVDIR)/Scripts
|
||||
EXE=.exe
|
||||
endif
|
||||
|
||||
|
||||
#
|
||||
# Virtual environment
|
||||
#
|
||||
|
||||
.PHONY: venv
|
||||
venv: $(VENV)/$(MARKER)
|
||||
|
||||
.PHONY: clean-venv
|
||||
clean-venv:
|
||||
-$(RM) -r "$(VENVDIR)"
|
||||
|
||||
.PHONY: show-venv
|
||||
show-venv: venv
|
||||
@$(VENV)/python -c "import sys; print('Python ' + sys.version.replace('\n',''))"
|
||||
@$(VENV)/pip --version
|
||||
@echo venv: $(VENVDIR)
|
||||
|
||||
.PHONY: debug-venv
|
||||
debug-venv:
|
||||
@$(MAKE) --version
|
||||
$(info PY="$(PY)")
|
||||
$(info REQUIREMENTS_TXT="$(REQUIREMENTS_TXT)")
|
||||
$(info VENVDIR="$(VENVDIR)")
|
||||
$(info VENVDEPENDS="$(VENVDEPENDS)")
|
||||
$(info WORKDIR="$(WORKDIR)")
|
||||
|
||||
|
||||
#
|
||||
# Dependencies
|
||||
#
|
||||
|
||||
ifneq ($(strip $(REQUIREMENTS_TXT)),)
|
||||
VENVDEPENDS+=$(REQUIREMENTS_TXT)
|
||||
endif
|
||||
|
||||
ifneq ($(wildcard setup.py),)
|
||||
VENVDEPENDS+=setup.py
|
||||
endif
|
||||
ifneq ($(wildcard setup.cfg),)
|
||||
VENVDEPENDS+=setup.cfg
|
||||
endif
|
||||
|
||||
$(VENV):
|
||||
$(PY) -m venv $(VENVDIR)
|
||||
$(VENV)/python -m pip install --upgrade pip setuptools wheel
|
||||
|
||||
$(VENV)/$(MARKER): $(VENVDEPENDS) | $(VENV)
|
||||
ifneq ($(strip $(REQUIREMENTS_TXT)),)
|
||||
$(VENV)/pip install $(foreach path,$(REQUIREMENTS_TXT),-r $(path))
|
||||
endif
|
||||
ifneq ($(wildcard setup.py),)
|
||||
$(VENV)/pip install -e .
|
||||
endif
|
||||
touch $(VENV)/$(MARKER)
|
||||
|
||||
|
||||
#
|
||||
# Interactive shells
|
||||
#
|
||||
|
||||
.PHONY: python
|
||||
python: venv
|
||||
exec $(VENV)/python
|
||||
|
||||
.PHONY: ipython
|
||||
ipython: $(VENV)/ipython
|
||||
exec $(VENV)/ipython
|
||||
|
||||
.PHONY: shell
|
||||
shell: venv
|
||||
. $(VENV)/activate && exec $(notdir $(SHELL))
|
||||
|
||||
.PHONY: bash zsh
|
||||
bash zsh: venv
|
||||
. $(VENV)/activate && exec $@
|
||||
|
||||
|
||||
#
|
||||
# Commandline tools (wildcard rule, executable name must match package name)
|
||||
#
|
||||
|
||||
ifneq ($(EXE),)
|
||||
$(VENV)/%: $(VENV)/%$(EXE) ;
|
||||
.PHONY: $(VENV)/%
|
||||
.PRECIOUS: $(VENV)/%$(EXE)
|
||||
endif
|
||||
|
||||
$(VENV)/%$(EXE): $(VENV)/$(MARKER)
|
||||
$(VENV)/pip install --upgrade $*
|
||||
touch $@
|
@ -10,7 +10,7 @@ aprsd_slack_plugin
|
||||
.. image:: https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336
|
||||
:target: https://timothycrosley.github.io/isort/
|
||||
|
||||
This project is a python plugin for the APRSD server daemon written by
|
||||
This project is a python plugin for the APRSD server daemon written by
|
||||
Craig Lamparter. The plugin looks for location APRS commands from a ham
|
||||
radio, then reports that location to a slack channel. This is basically a
|
||||
location proxy.
|
||||
|
@ -1,5 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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
|
||||
|
@ -1,103 +0,0 @@
|
||||
import logging
|
||||
|
||||
from aprsd import plugin
|
||||
from slack_sdk import WebClient
|
||||
from slack_sdk.errors import SlackApiError
|
||||
|
||||
import aprsd_slack_plugin
|
||||
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
class SlackCommandPlugin(plugin.APRSDPluginBase):
|
||||
"""SlackCommandPlugin.
|
||||
|
||||
This APRSD plugin looks for the location command comming in
|
||||
to aprsd, then fetches the caller's location, and then reports
|
||||
that location string to the configured slack channel.
|
||||
|
||||
To use this:
|
||||
Create a slack bot for your workspace at api.slack.com.
|
||||
A good source of information on how to create the app
|
||||
and the tokens and permissions and install the app in your
|
||||
workspace is here:
|
||||
|
||||
https://api.slack.com/start/building/bolt-python
|
||||
|
||||
|
||||
You will need the signing secret from the
|
||||
Basic Information -> App Credentials form.
|
||||
You will also need the Bot User OAuth Access Token from
|
||||
OAuth & Permissions -> OAuth Tokens for Your Team ->
|
||||
Bot User OAuth Access Token.
|
||||
|
||||
Install the app/bot into your workspace.
|
||||
|
||||
Edit your ~/.config/aprsd/aprsd.yml and add the section
|
||||
slack:
|
||||
signing_secret: <signing secret token here>
|
||||
bot_token: <Bot User OAuth Access Token here>
|
||||
channel: <channel name here>
|
||||
"""
|
||||
|
||||
version = aprsd_slack_plugin.__version__
|
||||
|
||||
# matches any string starting with h or H
|
||||
command_regex = "^[lL]"
|
||||
command_name = "location-slack"
|
||||
|
||||
def _setup_slack(self):
|
||||
"""Create the slack require client from config."""
|
||||
|
||||
# signing_secret = self.config["slack"]["signing_secret"]
|
||||
if "slack" not in self.config:
|
||||
LOG.error("APRSD config is missing slack section")
|
||||
return False
|
||||
|
||||
bot_token = self.config["slack"].get("bot_token", None)
|
||||
if not bot_token:
|
||||
LOG.error(
|
||||
"APRSD config is missing slack: bot_token:<token>. "
|
||||
"Please install the slack app and get the "
|
||||
"Bot User OAth Access Token."
|
||||
)
|
||||
return False
|
||||
|
||||
self.swc = WebClient(token=bot_token)
|
||||
|
||||
self.slack_channel = self.config["slack"].get("channel", None)
|
||||
if not self.slack_channel:
|
||||
LOG.error(
|
||||
"APRSD config is missing slack: slack_channel: <name> "
|
||||
"Please add a slack channel name to send messages."
|
||||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def command(self, fromcall, message, ack):
|
||||
LOG.info("SlackCommandPlugin")
|
||||
|
||||
is_setup = self._setup_slack()
|
||||
if not is_setup:
|
||||
return
|
||||
|
||||
# now call the location plugin to get the location info
|
||||
location_plugin = plugin.LocationPlugin(self.config)
|
||||
location = location_plugin.command(fromcall, message, ack)
|
||||
if location:
|
||||
reply = location
|
||||
|
||||
LOG.debug("Sending '{}' to slack channel '{}'".format(reply, self.slack_channel))
|
||||
try:
|
||||
self.swc.chat_postMessage(channel=self.slack_channel, text=reply)
|
||||
except SlackApiError as e:
|
||||
LOG.error(
|
||||
"Failed to send message to channel '{}' because '{}'".format(
|
||||
self.slack_channel, str(e)
|
||||
)
|
||||
)
|
||||
else:
|
||||
LOG.debug("SlackCommandPlugin couldn't get location for '{}'".format(fromcall))
|
||||
|
||||
return None
|
70
aprsd_slack_plugin/base_plugin.py
Normal file
70
aprsd_slack_plugin/base_plugin.py
Normal file
@ -0,0 +1,70 @@
|
||||
import logging
|
||||
|
||||
from oslo_config import cfg
|
||||
from slack_sdk import WebClient
|
||||
|
||||
import aprsd_slack_plugin
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
class SlackPluginBase:
|
||||
"""SlackCommandPlugin.
|
||||
|
||||
This APRSD plugin looks for the location command comming in
|
||||
to aprsd, then fetches the caller's location, and then reports
|
||||
that location string to the configured slack channel.
|
||||
|
||||
To use this:
|
||||
Create a slack bot for your workspace at api.slack.com.
|
||||
A good source of information on how to create the app
|
||||
and the tokens and permissions and install the app in your
|
||||
workspace is here:
|
||||
|
||||
https://api.slack.com/start/building/bolt-python
|
||||
|
||||
|
||||
You will need the signing secret from the
|
||||
Basic Information -> App Credentials form.
|
||||
You will also need the Bot User OAuth Access Token from
|
||||
OAuth & Permissions -> OAuth Tokens for Your Team ->
|
||||
Bot User OAuth Access Token.
|
||||
|
||||
Install the app/bot into your workspace.
|
||||
|
||||
Edit your ~/.config/aprsd/aprsd.yml and add the section
|
||||
slack:
|
||||
signing_secret: <signing secret token here>
|
||||
bot_token: <Bot User OAuth Access Token here>
|
||||
channel: <channel name here>
|
||||
"""
|
||||
|
||||
version = aprsd_slack_plugin.__version__
|
||||
swc = None
|
||||
slack_channels = None
|
||||
|
||||
def setup_slack(self):
|
||||
"""Create the slack require client from config."""
|
||||
|
||||
if not CONF.aprsd_slack_plugin.signing_secret:
|
||||
LOG.error("Failed to find config aprsd_slack_plugin.signing_secret")
|
||||
return "No slack signing_secret found"
|
||||
|
||||
if not CONF.aprsd_slack_plugin.bot_token:
|
||||
LOG.error(
|
||||
"APRSD config is missing aprsd_slack_plugin.bot_token. "
|
||||
"Please install the slack app and get the "
|
||||
"Bot User OAth Access Token.",
|
||||
)
|
||||
return False
|
||||
|
||||
if not CONF.aprsd_slack_plugin.channels:
|
||||
LOG.error("aprsd_slack_plugin.channels is missing")
|
||||
return False
|
||||
|
||||
self.swc = WebClient(token=CONF.aprsd_slack_plugin.bot_token)
|
||||
self.slack_channels = CONF.aprsd_slack_plugin.channels
|
||||
|
||||
return True
|
7
aprsd_slack_plugin/conf/__init__.py
Normal file
7
aprsd_slack_plugin/conf/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
from oslo_config import cfg
|
||||
|
||||
from aprsd_slack_plugin.conf import slack
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
slack.register_opts(CONF)
|
81
aprsd_slack_plugin/conf/opts.py
Normal file
81
aprsd_slack_plugin/conf/opts.py
Normal file
@ -0,0 +1,81 @@
|
||||
# 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)
|
41
aprsd_slack_plugin/conf/slack.py
Normal file
41
aprsd_slack_plugin/conf/slack.py
Normal file
@ -0,0 +1,41 @@
|
||||
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,
|
||||
}
|
168
aprsd_slack_plugin/location_plugin.py
Normal file
168
aprsd_slack_plugin/location_plugin.py
Normal file
@ -0,0 +1,168 @@
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
|
||||
from aprsd import packets, plugin, plugin_utils
|
||||
from oslo_config import cfg
|
||||
|
||||
import aprsd_slack_plugin
|
||||
from aprsd_slack_plugin import base_plugin
|
||||
from aprsd_slack_plugin import conf # noqa
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
class SlackLocationPlugin(
|
||||
base_plugin.SlackPluginBase,
|
||||
plugin.APRSDRegexCommandPluginBase,
|
||||
plugin.APRSFIKEYMixin,
|
||||
):
|
||||
"""SlackCommandPlugin.
|
||||
|
||||
This APRSD plugin looks for the location command comming in
|
||||
to aprsd, then fetches the caller's location, and then reports
|
||||
that location string to the configured slack channel.
|
||||
|
||||
To use this:
|
||||
Create a slack bot for your workspace at api.slack.com.
|
||||
A good source of information on how to create the app
|
||||
and the tokens and permissions and install the app in your
|
||||
workspace is here:
|
||||
|
||||
https://api.slack.com/start/building/bolt-python
|
||||
|
||||
|
||||
You will need the signing secret from the
|
||||
Basic Information -> App Credentials form.
|
||||
You will also need the Bot User OAuth Access Token from
|
||||
OAuth & Permissions -> OAuth Tokens for Your Team ->
|
||||
Bot User OAuth Access Token.
|
||||
|
||||
Install the app/bot into your workspace.
|
||||
|
||||
Edit your ~/.config/aprsd/aprsd.conf and add the section
|
||||
slack:
|
||||
signing_secret: <signing secret token here>
|
||||
bot_token: <Bot User OAuth Access Token here>
|
||||
channel: <channel name here>
|
||||
"""
|
||||
|
||||
version = aprsd_slack_plugin.__version__
|
||||
|
||||
# matches any string starting with h or H
|
||||
command_regex = "^[lL]"
|
||||
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):
|
||||
LOG.info("SlackCommandPlugin")
|
||||
|
||||
fromcall = packet.from_call
|
||||
message = packet.message_text
|
||||
|
||||
# get last location of a callsign, get descriptive name from weather service
|
||||
api_key = CONF.aprs_fi.apiKey
|
||||
|
||||
# optional second argument is a callsign to search
|
||||
a = re.search(r"^.*\s+(.*)", message)
|
||||
if a is not None:
|
||||
searchcall = a.group(1)
|
||||
searchcall = searchcall.upper()
|
||||
else:
|
||||
# if no second argument, search for calling station
|
||||
searchcall = fromcall
|
||||
|
||||
try:
|
||||
aprs_data = plugin_utils.get_aprs_fi(api_key, searchcall)
|
||||
except Exception as ex:
|
||||
LOG.error(f"Failed to fetch aprs.fi '{ex}'")
|
||||
return "Failed to fetch aprs.fi location"
|
||||
|
||||
LOG.debug(f"LocationPlugin: aprs_data = {aprs_data}")
|
||||
if not len(aprs_data["entries"]):
|
||||
LOG.error("Didn't get any entries from aprs.fi")
|
||||
return "Failed to fetch aprs.fi location"
|
||||
|
||||
lat = aprs_data["entries"][0]["lat"]
|
||||
lon = aprs_data["entries"][0]["lng"]
|
||||
try: # altitude not always provided
|
||||
alt = float(aprs_data["entries"][0]["altitude"])
|
||||
except Exception:
|
||||
alt = 0
|
||||
altfeet = int(alt * 3.28084)
|
||||
aprs_lasttime_seconds = aprs_data["entries"][0]["lasttime"]
|
||||
delta_seconds = time.time() - int(aprs_lasttime_seconds)
|
||||
delta_hours = delta_seconds / 60 / 60
|
||||
|
||||
wx_data = None
|
||||
try:
|
||||
wx_data = plugin_utils.get_weather_gov_for_gps(lat, lon)
|
||||
except Exception:
|
||||
LOG.warning("Couldn't fetch forecast.weather.gov")
|
||||
|
||||
callsign_url = f"<http://aprs.fi/info/a/{searchcall}|{searchcall}>"
|
||||
|
||||
aprs_url = "<http://aprs.fi/#!mt=roadmap&z=15&lat={}&lng={}|" " http://aprs.fi/>".format(
|
||||
lat,
|
||||
lon,
|
||||
)
|
||||
|
||||
message = {}
|
||||
message["username"] = "APRSD - Slack Location Plugin"
|
||||
message["icon_emoji"] = ":satellite_antenna:"
|
||||
message["attachments"] = [{}]
|
||||
message["text"] = f"{callsign_url} - Location"
|
||||
message["channel"] = "#random"
|
||||
|
||||
attachment = message["attachments"][0]
|
||||
attachment["fallback"] = message["text"]
|
||||
attachment["fields"] = []
|
||||
|
||||
# if the coordinates are outside of the US, we don't get this
|
||||
# aread description
|
||||
if wx_data and "location" in wx_data and "areaDescription" in wx_data["location"]:
|
||||
attachment["fields"].append(
|
||||
{
|
||||
"title": "Location",
|
||||
"value": wx_data["location"]["areaDescription"],
|
||||
"short": True,
|
||||
},
|
||||
)
|
||||
|
||||
attachment["fields"].append(
|
||||
{"title": "Map Location", "value": aprs_url, "short": True},
|
||||
)
|
||||
attachment["fields"].append(
|
||||
{
|
||||
"title": "Altitude",
|
||||
"value": altfeet,
|
||||
"short": True,
|
||||
"fallback": f"Altitude - {altfeet}",
|
||||
},
|
||||
)
|
||||
attachment["fields"].append(
|
||||
{
|
||||
"title": "Time",
|
||||
"value": f"{round(delta_hours, 1)} h ago",
|
||||
"short": True,
|
||||
"fallback": f"Time {round(delta_hours, 1)} h ago",
|
||||
},
|
||||
)
|
||||
|
||||
LOG.debug(message)
|
||||
|
||||
# self.swc.chat_postMessage(**message)
|
||||
for channel in self.slack_channels:
|
||||
message["channel"] = channel
|
||||
self.swc.chat_postMessage(**message)
|
||||
|
||||
# Don't have aprsd try and send a reply
|
||||
return packets.NULL_MESSAGE
|
87
aprsd_slack_plugin/message_plugin.py
Normal file
87
aprsd_slack_plugin/message_plugin.py
Normal file
@ -0,0 +1,87 @@
|
||||
import logging
|
||||
import re
|
||||
|
||||
from aprsd import packets
|
||||
from oslo_config import cfg
|
||||
|
||||
import aprsd_slack_plugin
|
||||
from aprsd_slack_plugin import base_plugin
|
||||
from aprsd_slack_plugin import conf # noqa
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
class SlackMessagePlugin(base_plugin.SlackPluginBase):
|
||||
"""SlackMessagePlugin.
|
||||
|
||||
This APRSD plugin looks for the slack msg command comming in
|
||||
to aprsd, then forwards the message to the configured slack channel.
|
||||
|
||||
To use this:
|
||||
Create a slack bot for your workspace at api.slack.com.
|
||||
A good source of information on how to create the app
|
||||
and the tokens and permissions and install the app in your
|
||||
workspace is here:
|
||||
|
||||
https://api.slack.com/start/building/bolt-python
|
||||
|
||||
|
||||
You will need the signing secret from the
|
||||
Basic Information -> App Credentials form.
|
||||
You will also need the Bot User OAuth Access Token from
|
||||
OAuth & Permissions -> OAuth Tokens for Your Team ->
|
||||
Bot User OAuth Access Token.
|
||||
|
||||
Install the app/bot into your workspace.
|
||||
|
||||
Edit your ~/.config/aprsd/aprsd.yml and add the section
|
||||
slack:
|
||||
signing_secret: <signing secret token here>
|
||||
bot_token: <Bot User OAuth Access Token here>
|
||||
channel: <channel name here>
|
||||
"""
|
||||
|
||||
version = aprsd_slack_plugin.__version__
|
||||
|
||||
# matches any string starting with h or H
|
||||
command_regex = r"^([s]|[s]\s|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):
|
||||
message = packet.message_text
|
||||
fromcall = packet.from_call
|
||||
LOG.info(f"SlackMessagePlugin '{message}'")
|
||||
|
||||
# optional second argument is a callsign to search
|
||||
a = re.search(r"^.*\s+(.*)", message)
|
||||
if a is not None:
|
||||
searchcall = a.group(1)
|
||||
searchcall = searchcall.upper()
|
||||
else:
|
||||
# if no second argument, search for calling station
|
||||
searchcall = fromcall
|
||||
|
||||
slack_message = {}
|
||||
slack_message["username"] = "APRSD - Slack Message Plugin"
|
||||
slack_message["icon_emoji"] = ":satellite_antenna:"
|
||||
slack_message["text"] = f"{fromcall} says {message}"
|
||||
slack_message["channel"] = "#random"
|
||||
|
||||
LOG.debug(slack_message)
|
||||
|
||||
self.swc.chat_postMessage(**slack_message)
|
||||
# for channel in self.slack_channels:
|
||||
# message["channel"] = channel
|
||||
# self.swc.chat_postMessage(**message)
|
||||
|
||||
# Don't have aprsd try and send a reply
|
||||
return packets.NULL_MESSAGE
|
56
aprsd_slack_plugin/notify_plugin.py
Normal file
56
aprsd_slack_plugin/notify_plugin.py
Normal file
@ -0,0 +1,56 @@
|
||||
import logging
|
||||
|
||||
from aprsd import packets, plugin
|
||||
from oslo_config import cfg
|
||||
|
||||
import aprsd_slack_plugin
|
||||
from aprsd_slack_plugin import base_plugin
|
||||
from aprsd_slack_plugin import conf # noqa
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger("APRSD")
|
||||
|
||||
|
||||
class SlackNotifyPlugin(
|
||||
base_plugin.SlackPluginBase,
|
||||
plugin.APRSDWatchListPluginBase,
|
||||
):
|
||||
"""SlackNotifyPlugin."""
|
||||
|
||||
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):
|
||||
LOG.info("SlackCommandPlugin")
|
||||
|
||||
fromcall = packet.from_call
|
||||
# message = packet["message_text"]
|
||||
|
||||
wl = packets.WatchList()
|
||||
if wl.is_old(fromcall):
|
||||
# get last location of a callsign, get descriptive name from weather service
|
||||
callsign_url = f"<http://aprs.fi/info/a/{fromcall}|{fromcall}>"
|
||||
|
||||
message = {}
|
||||
message["username"] = "APRSD - Slack Notification Plugin"
|
||||
message["icon_emoji"] = ":satellite_antenna:"
|
||||
message["attachments"] = [{}]
|
||||
message["text"] = f"{callsign_url} - Is now on APRS"
|
||||
message["channel"] = "#hemna"
|
||||
|
||||
LOG.debug(message)
|
||||
|
||||
# self.swc.chat_postMessage(**message)
|
||||
for channel in self.slack_channels:
|
||||
message["channel"] = channel
|
||||
self.swc.chat_postMessage(**message)
|
||||
|
||||
# Don't have aprsd try and send a reply
|
||||
return packets.NULL_MESSAGE
|
@ -1,11 +0,0 @@
|
||||
tox
|
||||
pytest
|
||||
pytest-cov
|
||||
mypy
|
||||
flake8
|
||||
pep8-naming
|
||||
black
|
||||
isort
|
||||
pbr
|
||||
Sphinx
|
||||
aprsd
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
|
2
gray.conf
Normal file
2
gray.conf
Normal file
@ -0,0 +1,2 @@
|
||||
formatters = add-trailing-comma,autoflake,fixit,isort,pyupgrade,unify
|
||||
min-python-version = 3.8
|
@ -9,3 +9,5 @@ isort
|
||||
pbr
|
||||
Sphinx
|
||||
aprsd
|
||||
pre-commit
|
||||
pip-tools
|
76
requirements-dev.txt
Normal file
76
requirements-dev.txt
Normal file
@ -0,0 +1,76 @@
|
||||
#
|
||||
# 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.13 # via sphinx
|
||||
aprsd==1.0.0 # via -r requirements-dev.in
|
||||
babel==2.12.1 # via sphinx
|
||||
black==23.3.0 # via -r requirements-dev.in
|
||||
build==0.10.0 # via pip-tools
|
||||
cachetools==5.3.1 # via tox
|
||||
certifi==2023.5.7 # via requests
|
||||
cfgv==3.3.1 # via pre-commit
|
||||
chardet==5.1.0 # via tox
|
||||
charset-normalizer==3.2.0 # via requests
|
||||
click==8.1.4 # via aprsd, black, click-completion, pip-tools
|
||||
click-completion==0.5.2 # via aprsd
|
||||
colorama==0.4.6 # via tox
|
||||
coverage[toml]==7.2.7 # via pytest-cov
|
||||
distlib==0.3.6 # via virtualenv
|
||||
docutils==0.20.1 # via sphinx
|
||||
exceptiongroup==1.1.2 # via pytest
|
||||
filelock==3.12.2 # via tox, virtualenv
|
||||
flake8==6.0.0 # via -r requirements-dev.in, pep8-naming
|
||||
identify==2.5.24 # via pre-commit
|
||||
idna==3.4 # via requests
|
||||
imagesize==1.4.1 # via sphinx
|
||||
imapclient==2.3.1 # via aprsd
|
||||
importlib-metadata==6.8.0 # via sphinx
|
||||
iniconfig==2.0.0 # via pytest
|
||||
isort==5.12.0 # via -r requirements-dev.in
|
||||
jinja2==3.1.2 # via click-completion, sphinx
|
||||
markupsafe==2.1.3 # via jinja2
|
||||
mccabe==0.7.0 # via flake8
|
||||
mypy==1.4.1 # via -r requirements-dev.in
|
||||
mypy-extensions==1.0.0 # via black, mypy
|
||||
nodeenv==1.8.0 # via pre-commit
|
||||
packaging==23.1 # via black, build, pyproject-api, pytest, sphinx, tox
|
||||
pathspec==0.11.1 # via black
|
||||
pbr==5.11.1 # via -r requirements-dev.in, aprsd
|
||||
pep8-naming==0.13.3 # via -r requirements-dev.in
|
||||
pip-tools==6.14.0 # via -r requirements-dev.in
|
||||
platformdirs==3.8.1 # via black, tox, virtualenv
|
||||
pluggy==1.2.0 # via aprsd, pytest, tox
|
||||
pre-commit==3.3.3 # via -r requirements-dev.in
|
||||
pycodestyle==2.10.0 # via flake8
|
||||
pyflakes==3.0.1 # via flake8
|
||||
pygments==2.15.1 # via sphinx
|
||||
pyproject-api==1.5.3 # via tox
|
||||
pyproject-hooks==1.0.0 # via build
|
||||
pytest==7.4.0 # via -r requirements-dev.in, pytest-cov
|
||||
pytest-cov==4.1.0 # via -r requirements-dev.in
|
||||
pyyaml==6.0 # via aprsd, pre-commit
|
||||
requests==2.31.0 # via aprsd, sphinx
|
||||
shellingham==1.5.0.post1 # via click-completion
|
||||
six==1.16.0 # via aprsd, click-completion, imapclient
|
||||
snowballstemmer==2.2.0 # via sphinx
|
||||
sphinx==7.0.1 # via -r requirements-dev.in
|
||||
sphinxcontrib-applehelp==1.0.4 # via sphinx
|
||||
sphinxcontrib-devhelp==1.0.2 # via sphinx
|
||||
sphinxcontrib-htmlhelp==2.0.1 # via sphinx
|
||||
sphinxcontrib-jsmath==1.0.1 # via sphinx
|
||||
sphinxcontrib-qthelp==1.0.3 # via sphinx
|
||||
sphinxcontrib-serializinghtml==1.1.5 # via sphinx
|
||||
tomli==2.0.1 # via black, build, coverage, mypy, pip-tools, pyproject-api, pyproject-hooks, pytest, tox
|
||||
tox==4.6.4 # via -r requirements-dev.in
|
||||
typing-extensions==4.7.1 # via black, mypy
|
||||
urllib3==2.0.3 # via requests
|
||||
virtualenv==20.23.1 # via pre-commit, tox
|
||||
wheel==0.40.0 # via pip-tools
|
||||
zipp==3.16.0 # via importlib-metadata
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
# pip
|
||||
# setuptools
|
@ -1,4 +0,0 @@
|
||||
pbr
|
||||
slack_sdk>=3.0
|
||||
slackeventsapi>=2.1.0
|
||||
aprsd
|
@ -1,12 +1,5 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile
|
||||
#
|
||||
aprsd # via -r requirements.in
|
||||
pbr # via -r requirements.in, aprsd
|
||||
requests # via aprsd
|
||||
six # via aprsd, click-completion, imapclient
|
||||
slack-sdk # via -r requirements.in
|
||||
slackeventsapi # via -r requirements.in
|
||||
pbr
|
||||
slack_sdk>=3.0
|
||||
slackeventsapi>=2.1.0
|
||||
aprsd
|
||||
oslo_config
|
||||
|
16
setup.cfg
16
setup.cfg
@ -1,11 +1,10 @@
|
||||
[metadata]
|
||||
name = aprsd_slack_plugin
|
||||
summary = Amateur radio APRS daemon which listens for messages and responds
|
||||
description-file =
|
||||
README.rst
|
||||
long-description-content-type = text/x-rst; charset=UTF-8
|
||||
long_description = file: README.rst
|
||||
long_description_content_type = text/x-rst
|
||||
author = Walter A. Boring IV
|
||||
author-email = something@somewhere.com
|
||||
author_email = something@somewhere.com
|
||||
license_file = LICENSE.txt
|
||||
classifier =
|
||||
Topic :: Communications :: Ham Radio
|
||||
Operating System :: POSIX :: Linux
|
||||
@ -14,6 +13,13 @@ classifier =
|
||||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: 3.9
|
||||
description_file =
|
||||
README.rst
|
||||
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]
|
||||
setup-hooks =
|
||||
|
@ -1,22 +1,17 @@
|
||||
import sys
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
from aprsd_slack_plugin import aprsd_slack_plugin as slack_plugin
|
||||
from aprsd import conf # noqa
|
||||
|
||||
if sys.version_info >= (3, 2):
|
||||
from unittest import mock
|
||||
else:
|
||||
import mock
|
||||
from aprsd_slack_plugin import conf as plugin_conf # noqa
|
||||
from aprsd_slack_plugin import location_plugin
|
||||
|
||||
|
||||
class TestPlugin(unittest.TestCase):
|
||||
@mock.patch.object(slack_plugin.SlackCommandPlugin, "command")
|
||||
@mock.patch.object(location_plugin.SlackLocationPlugin, "filter")
|
||||
def test_plugin(self, mock_command):
|
||||
mock_command.return_value = ""
|
||||
|
||||
config = {
|
||||
"slack": {"signing_secret": "something", "bot_token": "sometoken", "channel": "hemna"}
|
||||
}
|
||||
|
||||
p = slack_plugin.SlackCommandPlugin(config)
|
||||
p.command("KM6LYW", "location", 1)
|
||||
p = location_plugin.SlackLocationPlugin()
|
||||
packet = {"from": "WB4BOR", "message_text": "location"}
|
||||
p.filter(packet)
|
||||
|
30
tox.ini
30
tox.ini
@ -11,18 +11,14 @@ extend-exclude =
|
||||
# This section is not needed if not using GitHub Actions for CI.
|
||||
[gh-actions]
|
||||
python =
|
||||
3.6: py36
|
||||
3.7: py37
|
||||
3.8: py38, fmt-check, lint
|
||||
3.9: py39
|
||||
|
||||
[tox]
|
||||
# These are the default environments that will be run
|
||||
# when ``tox`` is run without arguments.
|
||||
envlist =
|
||||
fmt-check
|
||||
lint
|
||||
py{36,37,38,39}
|
||||
py{39}
|
||||
skip_missing_interpreters = true
|
||||
|
||||
# Activate isolated build environment. tox will use a virtual environment
|
||||
@ -33,7 +29,7 @@ isolated_build = true
|
||||
[testenv]
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/dev-requirements.txt
|
||||
-r{toxinidir}/requirements-dev.txt
|
||||
commands =
|
||||
# Use -bb to enable BytesWarnings as error to catch str/bytes misuse.
|
||||
# Use -Werror to treat warnings as errors.
|
||||
@ -43,14 +39,14 @@ commands =
|
||||
skip_install = true
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/dev-requirements.txt
|
||||
-r{toxinidir}/requirements-dev.txt
|
||||
commands =
|
||||
mypy src tests
|
||||
|
||||
[testenv:lint]
|
||||
skip_install = true
|
||||
deps =
|
||||
-r{toxinidir}/dev-requirements.txt
|
||||
-r{toxinidir}/requirements-dev.txt
|
||||
commands =
|
||||
flake8 aprsd_slack_plugin tests
|
||||
|
||||
@ -58,29 +54,13 @@ commands =
|
||||
skip_install = true
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/dev-requirements.txt
|
||||
-r{toxinidir}/requirements-dev.txt
|
||||
changedir = {toxinidir}/docs
|
||||
commands =
|
||||
{envpython} clean_docs.py
|
||||
sphinx-apidoc --force --output-dir apidoc {toxinidir}/aprsd_slack_plugin
|
||||
sphinx-build -a -W . _build
|
||||
|
||||
[testenv:fmt]
|
||||
skip_install = true
|
||||
deps =
|
||||
-r{toxinidir}/dev-requirements.txt
|
||||
commands =
|
||||
isort aprsd_slack_plugin tests
|
||||
black aprsd_slack_plugin tests
|
||||
|
||||
[testenv:fmt-check]
|
||||
skip_install = true
|
||||
deps =
|
||||
-r{toxinidir}/dev-requirements.txt
|
||||
commands =
|
||||
isort --check-only aprsd_slack_plugin tests
|
||||
black --check aprsd_slack_plugin tests
|
||||
|
||||
[testenv:licenses]
|
||||
skip_install = true
|
||||
recreate = true
|
||||
|
Loading…
Reference in New Issue
Block a user