mirror of
https://github.com/craigerl/aprsd.git
synced 2025-04-09 21:18:37 -04:00
Added new HelpPlugin
This patch adds the always enabled HelpPlugin. This plugin now will respond to the 'help' or 'h' commands that will automatically build a help string based on the number of enabled plugins. It will also respond to help <plugin> with the plugin specific help
This commit is contained in:
parent
2bf85db21b
commit
3b7924b13d
19
aprsd/dev.py
19
aprsd/dev.py
@ -178,12 +178,22 @@ def setup_logging(config, loglevel, quiet):
|
||||
default="aprsd.plugins.wx.WxPlugin",
|
||||
help="The plugin to run",
|
||||
)
|
||||
@click.option(
|
||||
"-a",
|
||||
"--all",
|
||||
"load_all",
|
||||
show_default=True,
|
||||
is_flag=True,
|
||||
default=False,
|
||||
help="Load all the plugins in config?",
|
||||
)
|
||||
@click.argument("fromcall")
|
||||
@click.argument("message", nargs=-1, required=True)
|
||||
def test_plugin(
|
||||
loglevel,
|
||||
config_file,
|
||||
plugin_path,
|
||||
load_all,
|
||||
fromcall,
|
||||
message,
|
||||
):
|
||||
@ -199,8 +209,12 @@ def test_plugin(
|
||||
client.Client(config)
|
||||
|
||||
pm = plugin.PluginManager(config)
|
||||
pm._init()
|
||||
if load_all:
|
||||
pm.setup_plugins()
|
||||
else:
|
||||
pm._init()
|
||||
obj = pm._create_class(plugin_path, plugin.APRSDPluginBase, config=config)
|
||||
# Register the plugin they wanted tested.
|
||||
pm._pluggy_pm.register(obj)
|
||||
login = config["aprs"]["login"]
|
||||
|
||||
@ -212,8 +226,9 @@ def test_plugin(
|
||||
}
|
||||
|
||||
reply = pm.run(packet)
|
||||
pm.stop()
|
||||
# Plugin might have threads, so lets stop them so we can exit.
|
||||
obj.stop_threads()
|
||||
# obj.stop_threads()
|
||||
LOG.info(f"Result = '{reply}'")
|
||||
|
||||
|
||||
|
@ -6,6 +6,7 @@ import inspect
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import textwrap
|
||||
import threading
|
||||
|
||||
import pluggy
|
||||
@ -62,7 +63,7 @@ class APRSDPluginBase(metaclass=abc.ABCMeta):
|
||||
self.config = config
|
||||
self.message_counter = 0
|
||||
self.setup()
|
||||
self.threads = self.create_threads()
|
||||
self.threads = self.create_threads() or []
|
||||
self.start_threads()
|
||||
|
||||
def start_threads(self):
|
||||
@ -93,6 +94,9 @@ class APRSDPluginBase(metaclass=abc.ABCMeta):
|
||||
def message_count(self):
|
||||
return self.message_counter
|
||||
|
||||
def help(self):
|
||||
return "Help!"
|
||||
|
||||
@abc.abstractmethod
|
||||
def setup(self):
|
||||
"""Do any plugin setup here."""
|
||||
@ -198,6 +202,12 @@ class APRSDRegexCommandPluginBase(APRSDPluginBase, metaclass=abc.ABCMeta):
|
||||
"""The regex to match from the caller"""
|
||||
raise NotImplementedError
|
||||
|
||||
def help(self):
|
||||
return "{}: {}".format(
|
||||
self.command_name.lower(),
|
||||
self.command_regex,
|
||||
)
|
||||
|
||||
def setup(self):
|
||||
"""Do any plugin setup here."""
|
||||
self.enabled = True
|
||||
@ -228,6 +238,7 @@ class APRSDRegexCommandPluginBase(APRSDPluginBase, metaclass=abc.ABCMeta):
|
||||
self.__class__, ex,
|
||||
),
|
||||
)
|
||||
LOG.exception(ex)
|
||||
if result:
|
||||
self.tx_inc()
|
||||
else:
|
||||
@ -236,6 +247,66 @@ class APRSDRegexCommandPluginBase(APRSDPluginBase, metaclass=abc.ABCMeta):
|
||||
return result
|
||||
|
||||
|
||||
class HelpPlugin(APRSDRegexCommandPluginBase):
|
||||
"""Help Plugin that is always enabled.
|
||||
|
||||
This plugin is in this file to prevent a circular import.
|
||||
"""
|
||||
|
||||
version = "1.0"
|
||||
command_regex = "^[hH]"
|
||||
command_name = "help"
|
||||
|
||||
def help(self):
|
||||
return "Help: send APRS help or help <plugin>"
|
||||
|
||||
def process(self, packet):
|
||||
LOG.info("HelpPlugin")
|
||||
# fromcall = packet.get("from")
|
||||
message = packet.get("message_text", None)
|
||||
# ack = packet.get("msgNo", "0")
|
||||
a = re.search(r"^.*\s+(.*)", message)
|
||||
command_name = None
|
||||
if a is not None:
|
||||
command_name = a.group(1).lower()
|
||||
|
||||
pm = PluginManager()
|
||||
|
||||
if command_name and "?" not in command_name:
|
||||
# user wants help for a specific plugin
|
||||
reply = None
|
||||
for p in pm.get_plugins():
|
||||
if (
|
||||
p.enabled and isinstance(p, APRSDRegexCommandPluginBase)
|
||||
and p.command_name.lower() == command_name
|
||||
):
|
||||
reply = p.help()
|
||||
|
||||
if reply:
|
||||
return reply
|
||||
|
||||
list = []
|
||||
for p in pm.get_plugins():
|
||||
LOG.debug(p)
|
||||
if p.enabled and isinstance(p, APRSDRegexCommandPluginBase):
|
||||
name = p.command_name.lower()
|
||||
if name not in list and "help" not in name:
|
||||
list.append(name)
|
||||
|
||||
list.sort()
|
||||
reply = " ".join(list)
|
||||
lines = textwrap.wrap(reply, 60)
|
||||
replies = ["Send APRS MSG of 'help' or 'help <plugin>'"]
|
||||
for line in lines:
|
||||
replies.append(f"plugins: {line}")
|
||||
|
||||
for entry in replies:
|
||||
LOG.debug(f"{len(entry)} {entry}")
|
||||
|
||||
LOG.debug(f"{replies}")
|
||||
return replies
|
||||
|
||||
|
||||
class PluginManager:
|
||||
# The singleton instance object for this class
|
||||
_instance = None
|
||||
@ -365,8 +436,12 @@ class PluginManager:
|
||||
"""Create the plugin manager and register plugins."""
|
||||
|
||||
LOG.info("Loading APRSD Plugins")
|
||||
enabled_plugins = self.config["aprsd"].get("enabled_plugins", None)
|
||||
self._init()
|
||||
# Help plugin is always enabled.
|
||||
_help = HelpPlugin(self.config)
|
||||
self._pluggy_pm.register(_help)
|
||||
|
||||
enabled_plugins = self.config["aprsd"].get("enabled_plugins", None)
|
||||
if enabled_plugins:
|
||||
for p_name in enabled_plugins:
|
||||
self._load_plugin(p_name)
|
||||
@ -392,6 +467,12 @@ class PluginManager:
|
||||
with self.lock:
|
||||
return self._pluggy_pm.hook.filter(packet=packet)
|
||||
|
||||
def stop(self):
|
||||
"""Stop all threads created by all plugins."""
|
||||
with self.lock:
|
||||
for p in self.get_plugins():
|
||||
p.stop_threads()
|
||||
|
||||
def register_msg(self, obj):
|
||||
"""Register the plugin."""
|
||||
self._pluggy_pm.register(obj)
|
||||
|
@ -1,6 +1,6 @@
|
||||
import logging
|
||||
|
||||
from aprsd import messaging, packets, plugin
|
||||
from aprsd import packets, plugin
|
||||
|
||||
|
||||
LOG = logging.getLogger("APRSD")
|
||||
@ -46,4 +46,3 @@ class NotifySeenPlugin(plugin.APRSDWatchListPluginBase):
|
||||
wl.max_delta(),
|
||||
),
|
||||
)
|
||||
return messaging.NULL_MESSAGE
|
||||
|
@ -54,7 +54,7 @@ class TimeOpenCageDataPlugin(TimePlugin):
|
||||
|
||||
version = "1.0"
|
||||
command_regex = "^[tT]"
|
||||
command_name = "Time"
|
||||
command_name = "time"
|
||||
|
||||
@trace.trace
|
||||
def process(self, packet):
|
||||
@ -120,7 +120,7 @@ class TimeOWMPlugin(TimePlugin):
|
||||
|
||||
version = "1.0"
|
||||
command_regex = "^[tT]"
|
||||
command_name = "Time"
|
||||
command_name = "time"
|
||||
|
||||
@trace.trace
|
||||
def process(self, packet):
|
||||
|
@ -25,7 +25,7 @@ class USWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
|
||||
version = "1.0"
|
||||
command_regex = "^[wW]"
|
||||
command_name = "weather"
|
||||
command_name = "USWeather"
|
||||
|
||||
@trace.trace
|
||||
def process(self, packet):
|
||||
@ -88,7 +88,7 @@ class USMetarPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
|
||||
version = "1.0"
|
||||
command_regex = "^[metar]"
|
||||
command_name = "Metar"
|
||||
command_name = "USMetar"
|
||||
|
||||
@trace.trace
|
||||
def process(self, packet):
|
||||
@ -180,7 +180,16 @@ class OWMWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
|
||||
version = "1.0"
|
||||
command_regex = "^[wW]"
|
||||
command_name = "Weather"
|
||||
command_name = "OpenWeatherMap"
|
||||
|
||||
def help(self):
|
||||
_help = [
|
||||
"openweathermap: Send {} to get weather "
|
||||
"from your location".format(self.command_regex),
|
||||
"openweathermap: Send {} <callsign> to get "
|
||||
"weather from <callsign>".format(self.command_regex),
|
||||
]
|
||||
return _help
|
||||
|
||||
@trace.trace
|
||||
def process(self, packet):
|
||||
@ -301,7 +310,16 @@ class AVWXWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
|
||||
|
||||
version = "1.0"
|
||||
command_regex = "^[mM]"
|
||||
command_name = "Weather"
|
||||
command_name = "AVWXWeather"
|
||||
|
||||
def help(self):
|
||||
_help = [
|
||||
"avwxweather: Send {} to get weather "
|
||||
"from your location".format(self.command_regex),
|
||||
"avwxweather: Send {} <callsign> to get "
|
||||
"weather from <callsign>".format(self.command_regex),
|
||||
]
|
||||
return _help
|
||||
|
||||
@trace.trace
|
||||
def process(self, packet):
|
||||
|
@ -302,12 +302,11 @@ class APRSDProcessPacketThread(APRSDThread):
|
||||
# If the message was for us and we didn't have a
|
||||
# response, then we send a usage statement.
|
||||
if tocall == self.config["aprs"]["login"] and not replied:
|
||||
reply = "Usage: weather, locate [call], time, fortune, ping"
|
||||
|
||||
LOG.warning("Sending help!")
|
||||
msg = messaging.TextMessage(
|
||||
self.config["aprs"]["login"],
|
||||
fromcall,
|
||||
reply,
|
||||
"Unknown command! Send 'help' message for help",
|
||||
)
|
||||
msg.send()
|
||||
except Exception as ex:
|
||||
|
Loading…
Reference in New Issue
Block a user