From d51347f48ed78a4c9537eee8842f61f134a84080 Mon Sep 17 00:00:00 2001 From: Hemna Date: Thu, 29 Dec 2022 14:44:34 -0500 Subject: [PATCH] Update for aprsd 3.0.0 This patch updates for using the oslo_config style CONF and declares the plugin config vars --- aprsd_twitter_plugin/conf/__init__.py | 10 ++++ aprsd_twitter_plugin/conf/opts.py | 80 +++++++++++++++++++++++++ aprsd_twitter_plugin/conf/twitter.py | 57 ++++++++++++++++++ aprsd_twitter_plugin/twitter.py | 86 +++++++++++++++++---------- requirements.txt | 3 +- setup.cfg | 4 ++ 6 files changed, 209 insertions(+), 31 deletions(-) create mode 100644 aprsd_twitter_plugin/conf/__init__.py create mode 100644 aprsd_twitter_plugin/conf/opts.py create mode 100644 aprsd_twitter_plugin/conf/twitter.py diff --git a/aprsd_twitter_plugin/conf/__init__.py b/aprsd_twitter_plugin/conf/__init__.py new file mode 100644 index 0000000..a6b8b80 --- /dev/null +++ b/aprsd_twitter_plugin/conf/__init__.py @@ -0,0 +1,10 @@ +import logging + +from oslo_config import cfg + +from aprsd_twitter_plugin.conf import twitter + + +CONF = cfg.CONF + +twitter.register_opts(CONF) diff --git a/aprsd_twitter_plugin/conf/opts.py b/aprsd_twitter_plugin/conf/opts.py new file mode 100644 index 0000000..d411b5e --- /dev/null +++ b/aprsd_twitter_plugin/conf/opts.py @@ -0,0 +1,80 @@ +# Copyright 2015 OpenStack Foundation +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +This is the single point of entry to generate the sample configuration +file for Nova. It collects all the necessary info from the other modules +in this package. It is assumed that: + +* every other module in this package has a 'list_opts' function which + return a dict where + * the keys are strings which are the group names + * the value of each key is a list of config options for that group +* the nova.conf package doesn't have further packages with config options +* this module is only used in the context of sample file generation +""" + +import collections +import importlib +import os +import pkgutil + + +LIST_OPTS_FUNC_NAME = "list_opts" + + +def _tupleize(dct): + """Take the dict of options and convert to the 2-tuple format.""" + return [(key, val) for key, val in dct.items()] + + +def list_opts(): + opts = collections.defaultdict(list) + module_names = _list_module_names() + imported_modules = _import_modules(module_names) + _append_config_options(imported_modules, opts) + return _tupleize(opts) + + +def _list_module_names(): + module_names = [] + package_path = os.path.dirname(os.path.abspath(__file__)) + for _, modname, ispkg in pkgutil.iter_modules(path=[package_path]): + if modname == "opts" or ispkg: + continue + else: + module_names.append(modname) + return module_names + + +def _import_modules(module_names): + imported_modules = [] + for modname in module_names: + mod = importlib.import_module("aprsd_twitter_plugin.conf." + modname) + if not hasattr(mod, LIST_OPTS_FUNC_NAME): + msg = "The module 'aprsd_twitter_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) diff --git a/aprsd_twitter_plugin/conf/twitter.py b/aprsd_twitter_plugin/conf/twitter.py new file mode 100644 index 0000000..66800ba --- /dev/null +++ b/aprsd_twitter_plugin/conf/twitter.py @@ -0,0 +1,57 @@ +from oslo_config import cfg + + +twitter_group = cfg.OptGroup( + name="aprsd_twitter_plugin", + title="APRSD Twitter Plugin settings", +) + +twitter_opts = [ + cfg.StrOpt( + "callsign", + help="Callsign allowed to send tweets! " + "Any callsign starting with this will be allowed to tweet to" + "the configured twitter account. " + "For example, if you set this to WB4BOR then any" + "callsign starting with WB4BOR will be allowed to tweet." + "This way WB4BOR-1 can tweet from this instance.", + ), + cfg.StrOpt( + "apiKey", + help="Your twitter apiKey" + "Information for creating your api keys is here: " + "https://developer.twitter.com/en/docs/authentication/oauth-1-0a/api-key-and-secret", + ), + cfg.StrOpt( + "apiKey_secret", + help="Your twitter accounts apikey secret.", + ), + cfg.StrOpt( + "access_token", + help="The twitter access_token for your Twitter account", + ), + cfg.StrOpt( + "access_token_secret", + help="The twitter access token secret for your Twitter account", + ), + cfg.BoolOpt( + "add_aprs_hashtag", + default=True, + help="Automatically add #aprs hash tag to every tweet?", + ), +] + +ALL_OPTS = ( + twitter_opts +) + + +def register_opts(cfg): + cfg.register_group(twitter_group) + cfg.register_opts(ALL_OPTS, group=twitter_group) + + +def list_opts(): + return { + twitter_group.name: ALL_OPTS, + } diff --git a/aprsd_twitter_plugin/twitter.py b/aprsd_twitter_plugin/twitter.py index 7c14643..1d25e25 100644 --- a/aprsd_twitter_plugin/twitter.py +++ b/aprsd_twitter_plugin/twitter.py @@ -1,11 +1,15 @@ import logging import tweepy -from aprsd import packets, plugin +from aprsd import conf # noqa +from aprsd import plugin +from oslo_config import cfg import aprsd_twitter_plugin +from aprsd_twitter_plugin import conf # noqa +CONF = cfg.CONF LOG = logging.getLogger("APRSD") @@ -13,7 +17,8 @@ class SendTweetPlugin(plugin.APRSDRegexCommandPluginBase): version = aprsd_twitter_plugin.__version__ # Look for any command that starts with tw or tW or TW or Tw - command_regex = "^[tT][wW]" + # or case insensitive version of 'twitter' + command_regex = r"^([t][w]\s|twitter)" # the command is for ? command_name = "tweet" @@ -30,33 +35,52 @@ class SendTweetPlugin(plugin.APRSDRegexCommandPluginBase): # Do some checks here? self.enabled = True + if not CONF.aprsd_twitter_plugin.callsign: + LOG.error( + "No aprsd_twitter_pligin.callsign is set." + " Callsign is needed to allow tweets!", + ) + self.enabled = False + # Ensure the access token exists. - if not self.config.exists("services.twitter.apiKey"): - LOG.error("No services.twitter.apiKey exists. Plugin Disabled.") + if not CONF.aprsd_twitter_plugin.apiKey: + LOG.error( + "No aprsd_twitter_plugin.apiKey is set!." + " Plugin Disabled.", + ) self.enabled = False - if not self.config.exists("services.twitter.apiKey_secret"): - LOG.error("No services.twitter.apiKey_secret exists. Plugin Disabled.") + if not CONF.aprsd_twitter_plugin.apiKey_secret: + LOG.error( + "No aprsd_twitter_plugin.apiKey_secret is set." + " Plugin Disabled.", + ) self.enabled = False - if not self.config.exists("services.twitter.access_token"): - LOG.error("No services.twitter.access_token exists. Plugin Disabled.") + if not CONF.aprsd_twitter_plugin.access_token: + LOG.error( + "No aprsd_twitter_plugin.access_token exists." + " Plugin Disabled.", + ) self.enabled = False - if not self.config.exists("services.twitter.access_token_secret"): - LOG.error("No services.twitter.access_token_secret exists. Plugin Disabled.") + if not CONF.aprsd_twitter_plugin.access_token_secret: + LOG.error( + "No aprsd_twitter_plugin.access_token_secret exists." + " Plugin Disabled.", + ) self.enabled = False def _create_client(self): """Create the twitter client object.""" auth = tweepy.OAuthHandler( - self.config.get("services.twitter.apiKey"), - self.config.get("services.twitter.apiKey_secret"), + CONF.aprsd_twitter_plugin.apiKey, + CONF.aprsd_twitter_plugin.apiKey_secret, ) auth.set_access_token( - self.config.get("services.twitter.access_token"), - self.config.get("services.twitter.access_token_secret"), + CONF.aprsd_twitter_plugin.access_token, + CONF.aprsd_twitter_plugin.access_token_secret, ) api = tweepy.API( @@ -86,23 +110,25 @@ class SendTweetPlugin(plugin.APRSDRegexCommandPluginBase): del message[0] message = " ".join(message) - if self.enabled: - # Now we can process - mycall = self.config["ham"]["callsign"] + # Now we can process + auth_call = CONF.aprsd_twitter_plugin.callsign - # Only allow the owner of aprsd to send a tweet - if not from_callsign.startswith(mycall): - return "Unauthorized" + # Only allow the owner of aprsd to send a tweet + if not from_callsign.startswith(auth_call): + return f"{from_callsign} not authorized to tweet!" - client = self._create_client() - if not client: - LOG.error("No twitter client!!") - return "Failed to Auth" + client = self._create_client() + if not client: + LOG.error("No twitter client!!") + return "Failed to Auth" - # Now lets tweet! - client.update_status(message) + if CONF.aprsd_twitter_plugin.add_aprs_hashtag: + message += ( + " #aprs #aprsd #hamradio " + "https://github.com/hemna/aprsd-twitter-plugin" + ) - return "Tweet sent!" - else: - LOG.warning("SendTweetPlugin is disabled.") - return packets.NULL_MESSAGE + # Now lets tweet! + client.update_status(message) + + return "Tweet sent!" diff --git a/requirements.txt b/requirements.txt index a3c279e..7ab8931 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ pbr -aprsd>=2.7.0 +aprsd>=3.0.0 tweepy +oslo-config diff --git a/setup.cfg b/setup.cfg index 5fd1043..0bc2efa 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,6 +19,10 @@ description_file = README.rst summary = Python APRSD plugin to send tweets +[options.entry_points] +oslo.config.opts = + aprsd_twitter_plugin.conf = aprsd_twitter_plugin.conf.opts:list_opts + [global] setup-hooks = pbr.hooks.setup_hook