mirror of
https://github.com/hemna/aprsd-slack-plugin.git
synced 2024-09-28 08:06:35 -04:00
Compare commits
No commits in common. "master" and "v1.0.3" have entirely different histories.
12
.github/FUNDING.yml
vendored
12
.github/FUNDING.yml
vendored
@ -1,12 +0,0 @@
|
|||||||
# 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
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: [3.7, 3.8, 3.9]
|
python-version: [3.6, 3.7, 3.8, 3.9]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
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
2
AUTHORS
@ -1,2 +0,0 @@
|
|||||||
Hemna <waboring@hemna.com>
|
|
||||||
Walter A. Boring IV <waboring@hemna.com>
|
|
44
ChangeLog
44
ChangeLog
@ -1,50 +1,6 @@
|
|||||||
CHANGES
|
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
|
v1.0.2
|
||||||
------
|
------
|
||||||
|
|
||||||
|
@ -173,3 +173,4 @@
|
|||||||
defend, and hold each Contributor harmless for any liability
|
defend, and hold each Contributor harmless for any liability
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
of your accepting any such warranty or additional liability.
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
73
Makefile
73
Makefile
@ -1,73 +0,0 @@
|
|||||||
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
183
Makefile.venv
@ -1,183 +0,0 @@
|
|||||||
#
|
|
||||||
# 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 $@
|
|
@ -1,3 +1,5 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
# a copy of the License at
|
# a copy of the License at
|
||||||
|
103
aprsd_slack_plugin/aprsd_slack_plugin.py
Normal file
103
aprsd_slack_plugin/aprsd_slack_plugin.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
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
|
@ -1,70 +0,0 @@
|
|||||||
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
|
|
@ -1,7 +0,0 @@
|
|||||||
from oslo_config import cfg
|
|
||||||
|
|
||||||
from aprsd_slack_plugin.conf import slack
|
|
||||||
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
|
||||||
slack.register_opts(CONF)
|
|
@ -1,81 +0,0 @@
|
|||||||
# 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)
|
|
@ -1,41 +0,0 @@
|
|||||||
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,
|
|
||||||
}
|
|
@ -1,168 +0,0 @@
|
|||||||
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
|
|
@ -1,87 +0,0 @@
|
|||||||
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
|
|
@ -1,56 +0,0 @@
|
|||||||
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
|
|
@ -9,5 +9,3 @@ isort
|
|||||||
pbr
|
pbr
|
||||||
Sphinx
|
Sphinx
|
||||||
aprsd
|
aprsd
|
||||||
pre-commit
|
|
||||||
pip-tools
|
|
11
dev-requirements.txt
Normal file
11
dev-requirements.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
tox
|
||||||
|
pytest
|
||||||
|
pytest-cov
|
||||||
|
mypy
|
||||||
|
flake8
|
||||||
|
pep8-naming
|
||||||
|
black
|
||||||
|
isort
|
||||||
|
pbr
|
||||||
|
Sphinx
|
||||||
|
aprsd
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
# Configuration file for the Sphinx documentation builder.
|
# Configuration file for the Sphinx documentation builder.
|
||||||
#
|
#
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
formatters = add-trailing-comma,autoflake,fixit,isort,pyupgrade,unify
|
|
||||||
min-python-version = 3.8
|
|
@ -1,76 +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.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
|
|
4
requirements.in
Normal file
4
requirements.in
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pbr
|
||||||
|
slack_sdk>=3.0
|
||||||
|
slackeventsapi>=2.1.0
|
||||||
|
aprsd
|
@ -1,5 +1,12 @@
|
|||||||
pbr
|
#
|
||||||
slack_sdk>=3.0
|
# This file is autogenerated by pip-compile
|
||||||
slackeventsapi>=2.1.0
|
# To update, run:
|
||||||
aprsd
|
#
|
||||||
oslo_config
|
# 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
|
||||||
|
16
setup.cfg
16
setup.cfg
@ -1,10 +1,11 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = aprsd_slack_plugin
|
name = aprsd_slack_plugin
|
||||||
long_description = file: README.rst
|
summary = Amateur radio APRS daemon which listens for messages and responds
|
||||||
long_description_content_type = text/x-rst
|
description-file =
|
||||||
|
README.rst
|
||||||
|
long-description-content-type = text/x-rst; charset=UTF-8
|
||||||
author = Walter A. Boring IV
|
author = Walter A. Boring IV
|
||||||
author_email = something@somewhere.com
|
author-email = something@somewhere.com
|
||||||
license_file = LICENSE.txt
|
|
||||||
classifier =
|
classifier =
|
||||||
Topic :: Communications :: Ham Radio
|
Topic :: Communications :: Ham Radio
|
||||||
Operating System :: POSIX :: Linux
|
Operating System :: POSIX :: Linux
|
||||||
@ -13,13 +14,6 @@ classifier =
|
|||||||
Programming Language :: Python :: 3.7
|
Programming Language :: Python :: 3.7
|
||||||
Programming Language :: Python :: 3.8
|
Programming Language :: Python :: 3.8
|
||||||
Programming Language :: Python :: 3.9
|
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]
|
[global]
|
||||||
setup-hooks =
|
setup-hooks =
|
||||||
|
@ -1,17 +1,22 @@
|
|||||||
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
from unittest import mock
|
|
||||||
|
|
||||||
from aprsd import conf # noqa
|
from aprsd_slack_plugin import aprsd_slack_plugin as slack_plugin
|
||||||
|
|
||||||
from aprsd_slack_plugin import conf as plugin_conf # noqa
|
if sys.version_info >= (3, 2):
|
||||||
from aprsd_slack_plugin import location_plugin
|
from unittest import mock
|
||||||
|
else:
|
||||||
|
import mock
|
||||||
|
|
||||||
|
|
||||||
class TestPlugin(unittest.TestCase):
|
class TestPlugin(unittest.TestCase):
|
||||||
@mock.patch.object(location_plugin.SlackLocationPlugin, "filter")
|
@mock.patch.object(slack_plugin.SlackCommandPlugin, "command")
|
||||||
def test_plugin(self, mock_command):
|
def test_plugin(self, mock_command):
|
||||||
mock_command.return_value = ""
|
mock_command.return_value = ""
|
||||||
|
|
||||||
p = location_plugin.SlackLocationPlugin()
|
config = {
|
||||||
packet = {"from": "WB4BOR", "message_text": "location"}
|
"slack": {"signing_secret": "something", "bot_token": "sometoken", "channel": "hemna"}
|
||||||
p.filter(packet)
|
}
|
||||||
|
|
||||||
|
p = slack_plugin.SlackCommandPlugin(config)
|
||||||
|
p.command("KM6LYW", "location", 1)
|
||||||
|
30
tox.ini
30
tox.ini
@ -11,14 +11,18 @@ extend-exclude =
|
|||||||
# This section is not needed if not using GitHub Actions for CI.
|
# This section is not needed if not using GitHub Actions for CI.
|
||||||
[gh-actions]
|
[gh-actions]
|
||||||
python =
|
python =
|
||||||
|
3.6: py36
|
||||||
|
3.7: py37
|
||||||
|
3.8: py38, fmt-check, lint
|
||||||
3.9: py39
|
3.9: py39
|
||||||
|
|
||||||
[tox]
|
[tox]
|
||||||
# These are the default environments that will be run
|
# These are the default environments that will be run
|
||||||
# when ``tox`` is run without arguments.
|
# when ``tox`` is run without arguments.
|
||||||
envlist =
|
envlist =
|
||||||
|
fmt-check
|
||||||
lint
|
lint
|
||||||
py{39}
|
py{36,37,38,39}
|
||||||
skip_missing_interpreters = true
|
skip_missing_interpreters = true
|
||||||
|
|
||||||
# Activate isolated build environment. tox will use a virtual environment
|
# Activate isolated build environment. tox will use a virtual environment
|
||||||
@ -29,7 +33,7 @@ isolated_build = true
|
|||||||
[testenv]
|
[testenv]
|
||||||
deps =
|
deps =
|
||||||
-r{toxinidir}/requirements.txt
|
-r{toxinidir}/requirements.txt
|
||||||
-r{toxinidir}/requirements-dev.txt
|
-r{toxinidir}/dev-requirements.txt
|
||||||
commands =
|
commands =
|
||||||
# Use -bb to enable BytesWarnings as error to catch str/bytes misuse.
|
# Use -bb to enable BytesWarnings as error to catch str/bytes misuse.
|
||||||
# Use -Werror to treat warnings as errors.
|
# Use -Werror to treat warnings as errors.
|
||||||
@ -39,14 +43,14 @@ commands =
|
|||||||
skip_install = true
|
skip_install = true
|
||||||
deps =
|
deps =
|
||||||
-r{toxinidir}/requirements.txt
|
-r{toxinidir}/requirements.txt
|
||||||
-r{toxinidir}/requirements-dev.txt
|
-r{toxinidir}/dev-requirements.txt
|
||||||
commands =
|
commands =
|
||||||
mypy src tests
|
mypy src tests
|
||||||
|
|
||||||
[testenv:lint]
|
[testenv:lint]
|
||||||
skip_install = true
|
skip_install = true
|
||||||
deps =
|
deps =
|
||||||
-r{toxinidir}/requirements-dev.txt
|
-r{toxinidir}/dev-requirements.txt
|
||||||
commands =
|
commands =
|
||||||
flake8 aprsd_slack_plugin tests
|
flake8 aprsd_slack_plugin tests
|
||||||
|
|
||||||
@ -54,13 +58,29 @@ commands =
|
|||||||
skip_install = true
|
skip_install = true
|
||||||
deps =
|
deps =
|
||||||
-r{toxinidir}/requirements.txt
|
-r{toxinidir}/requirements.txt
|
||||||
-r{toxinidir}/requirements-dev.txt
|
-r{toxinidir}/dev-requirements.txt
|
||||||
changedir = {toxinidir}/docs
|
changedir = {toxinidir}/docs
|
||||||
commands =
|
commands =
|
||||||
{envpython} clean_docs.py
|
{envpython} clean_docs.py
|
||||||
sphinx-apidoc --force --output-dir apidoc {toxinidir}/aprsd_slack_plugin
|
sphinx-apidoc --force --output-dir apidoc {toxinidir}/aprsd_slack_plugin
|
||||||
sphinx-build -a -W . _build
|
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]
|
[testenv:licenses]
|
||||||
skip_install = true
|
skip_install = true
|
||||||
recreate = true
|
recreate = true
|
||||||
|
Loading…
Reference in New Issue
Block a user