aprsd 1.0.0__py3-none-any.whl → 3.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aprsd/__init__.py +6 -4
- aprsd/cli_helper.py +151 -0
- aprsd/client/__init__.py +13 -0
- aprsd/client/aprsis.py +132 -0
- aprsd/client/base.py +105 -0
- aprsd/client/drivers/__init__.py +0 -0
- aprsd/client/drivers/aprsis.py +224 -0
- aprsd/client/drivers/fake.py +73 -0
- aprsd/client/drivers/kiss.py +119 -0
- aprsd/client/factory.py +88 -0
- aprsd/client/fake.py +48 -0
- aprsd/client/kiss.py +103 -0
- aprsd/client/stats.py +38 -0
- aprsd/cmds/__init__.py +0 -0
- aprsd/cmds/completion.py +22 -0
- aprsd/cmds/dev.py +162 -0
- aprsd/cmds/fetch_stats.py +156 -0
- aprsd/cmds/healthcheck.py +86 -0
- aprsd/cmds/list_plugins.py +319 -0
- aprsd/cmds/listen.py +230 -0
- aprsd/cmds/send_message.py +174 -0
- aprsd/cmds/server.py +142 -0
- aprsd/cmds/webchat.py +681 -0
- aprsd/conf/__init__.py +56 -0
- aprsd/conf/client.py +131 -0
- aprsd/conf/common.py +302 -0
- aprsd/conf/log.py +65 -0
- aprsd/conf/opts.py +80 -0
- aprsd/conf/plugin_common.py +191 -0
- aprsd/conf/plugin_email.py +105 -0
- aprsd/exception.py +13 -0
- aprsd/log/__init__.py +0 -0
- aprsd/log/log.py +138 -0
- aprsd/main.py +104 -867
- aprsd/messaging.py +4 -0
- aprsd/packets/__init__.py +12 -0
- aprsd/packets/collector.py +56 -0
- aprsd/packets/core.py +823 -0
- aprsd/packets/log.py +143 -0
- aprsd/packets/packet_list.py +116 -0
- aprsd/packets/seen_list.py +54 -0
- aprsd/packets/tracker.py +109 -0
- aprsd/packets/watch_list.py +122 -0
- aprsd/plugin.py +475 -284
- aprsd/plugin_utils.py +86 -0
- aprsd/plugins/__init__.py +0 -0
- aprsd/plugins/email.py +709 -0
- aprsd/plugins/fortune.py +61 -0
- aprsd/plugins/location.py +179 -0
- aprsd/plugins/notify.py +61 -0
- aprsd/plugins/ping.py +31 -0
- aprsd/plugins/time.py +115 -0
- aprsd/plugins/version.py +31 -0
- aprsd/plugins/weather.py +405 -0
- aprsd/stats/__init__.py +20 -0
- aprsd/stats/app.py +49 -0
- aprsd/stats/collector.py +38 -0
- aprsd/threads/__init__.py +11 -0
- aprsd/threads/aprsd.py +119 -0
- aprsd/threads/keep_alive.py +124 -0
- aprsd/threads/log_monitor.py +121 -0
- aprsd/threads/registry.py +56 -0
- aprsd/threads/rx.py +354 -0
- aprsd/threads/stats.py +44 -0
- aprsd/threads/tx.py +255 -0
- aprsd/utils/__init__.py +163 -0
- aprsd/utils/counter.py +51 -0
- aprsd/utils/json.py +80 -0
- aprsd/utils/objectstore.py +123 -0
- aprsd/utils/ring_buffer.py +40 -0
- aprsd/utils/trace.py +180 -0
- aprsd/web/__init__.py +0 -0
- aprsd/web/admin/__init__.py +0 -0
- aprsd/web/admin/static/css/index.css +84 -0
- aprsd/web/admin/static/css/prism.css +4 -0
- aprsd/web/admin/static/css/tabs.css +35 -0
- aprsd/web/admin/static/images/Untitled.png +0 -0
- aprsd/web/admin/static/images/aprs-symbols-16-0.png +0 -0
- aprsd/web/admin/static/images/aprs-symbols-16-1.png +0 -0
- aprsd/web/admin/static/images/aprs-symbols-64-0.png +0 -0
- aprsd/web/admin/static/images/aprs-symbols-64-1.png +0 -0
- aprsd/web/admin/static/images/aprs-symbols-64-2.png +0 -0
- aprsd/web/admin/static/js/charts.js +235 -0
- aprsd/web/admin/static/js/echarts.js +465 -0
- aprsd/web/admin/static/js/logs.js +26 -0
- aprsd/web/admin/static/js/main.js +231 -0
- aprsd/web/admin/static/js/prism.js +12 -0
- aprsd/web/admin/static/js/send-message.js +114 -0
- aprsd/web/admin/static/js/tabs.js +28 -0
- aprsd/web/admin/templates/index.html +196 -0
- aprsd/web/chat/static/css/chat.css +115 -0
- aprsd/web/chat/static/css/index.css +66 -0
- aprsd/web/chat/static/css/style.css.map +1 -0
- aprsd/web/chat/static/css/tabs.css +41 -0
- aprsd/web/chat/static/css/upstream/bootstrap.min.css +6 -0
- aprsd/web/chat/static/css/upstream/font.woff2 +0 -0
- aprsd/web/chat/static/css/upstream/google-fonts.css +23 -0
- aprsd/web/chat/static/css/upstream/jquery-ui.css +1311 -0
- aprsd/web/chat/static/css/upstream/jquery.toast.css +28 -0
- aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Bold.woff +0 -0
- aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Bold.woff2 +0 -0
- aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Regular.woff +0 -0
- aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Regular.woff2 +0 -0
- aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/icons.woff +0 -0
- aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/icons.woff2 +0 -0
- aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/outline-icons.woff +0 -0
- aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/outline-icons.woff2 +0 -0
- aprsd/web/chat/static/images/Untitled.png +0 -0
- aprsd/web/chat/static/images/aprs-symbols-16-0.png +0 -0
- aprsd/web/chat/static/images/aprs-symbols-16-1.png +0 -0
- aprsd/web/chat/static/images/aprs-symbols-64-0.png +0 -0
- aprsd/web/chat/static/images/aprs-symbols-64-1.png +0 -0
- aprsd/web/chat/static/images/aprs-symbols-64-2.png +0 -0
- aprsd/web/chat/static/images/globe.svg +3 -0
- aprsd/web/chat/static/js/gps.js +84 -0
- aprsd/web/chat/static/js/main.js +45 -0
- aprsd/web/chat/static/js/send-message.js +585 -0
- aprsd/web/chat/static/js/tabs.js +28 -0
- aprsd/web/chat/static/js/upstream/bootstrap.bundle.min.js +7 -0
- aprsd/web/chat/static/js/upstream/jquery-3.7.1.min.js +2 -0
- aprsd/web/chat/static/js/upstream/jquery-ui.min.js +13 -0
- aprsd/web/chat/static/js/upstream/jquery.toast.js +374 -0
- aprsd/web/chat/static/js/upstream/semantic.min.js +11 -0
- aprsd/web/chat/static/js/upstream/socket.io.min.js +7 -0
- aprsd/web/chat/templates/index.html +139 -0
- aprsd/wsgi.py +315 -0
- aprsd-3.4.1.dist-info/AUTHORS +13 -0
- aprsd-3.4.1.dist-info/LICENSE +175 -0
- aprsd-3.4.1.dist-info/METADATA +799 -0
- aprsd-3.4.1.dist-info/RECORD +134 -0
- {aprsd-1.0.0.dist-info → aprsd-3.4.1.dist-info}/WHEEL +1 -1
- aprsd-3.4.1.dist-info/entry_points.txt +8 -0
- aprsd/fake_aprs.py +0 -83
- aprsd/utils.py +0 -166
- aprsd-1.0.0.dist-info/AUTHORS +0 -6
- aprsd-1.0.0.dist-info/METADATA +0 -181
- aprsd-1.0.0.dist-info/RECORD +0 -13
- aprsd-1.0.0.dist-info/entry_points.txt +0 -4
- aprsd-1.0.0.dist-info/pbr.json +0 -1
- /aprsd/{fuzzyclock.py → utils/fuzzyclock.py} +0 -0
- {aprsd-1.0.0.dist-info → aprsd-3.4.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,156 @@
|
|
1
|
+
# Fetch active stats from a remote running instance of aprsd admin web interface.
|
2
|
+
import logging
|
3
|
+
|
4
|
+
import click
|
5
|
+
from oslo_config import cfg
|
6
|
+
import requests
|
7
|
+
from rich.console import Console
|
8
|
+
from rich.table import Table
|
9
|
+
|
10
|
+
# local imports here
|
11
|
+
import aprsd
|
12
|
+
from aprsd import cli_helper
|
13
|
+
from aprsd.main import cli
|
14
|
+
|
15
|
+
|
16
|
+
# setup the global logger
|
17
|
+
# log.basicConfig(level=log.DEBUG) # level=10
|
18
|
+
LOG = logging.getLogger("APRSD")
|
19
|
+
CONF = cfg.CONF
|
20
|
+
|
21
|
+
|
22
|
+
@cli.command()
|
23
|
+
@cli_helper.add_options(cli_helper.common_options)
|
24
|
+
@click.option(
|
25
|
+
"--host", type=str,
|
26
|
+
default=None,
|
27
|
+
help="IP address of the remote aprsd admin web ui fetch stats from.",
|
28
|
+
)
|
29
|
+
@click.option(
|
30
|
+
"--port", type=int,
|
31
|
+
default=None,
|
32
|
+
help="Port of the remote aprsd web admin interface to fetch stats from.",
|
33
|
+
)
|
34
|
+
@click.pass_context
|
35
|
+
@cli_helper.process_standard_options
|
36
|
+
def fetch_stats(ctx, host, port):
|
37
|
+
"""Fetch stats from a APRSD admin web interface."""
|
38
|
+
console = Console()
|
39
|
+
console.print(f"APRSD Fetch-Stats started version: {aprsd.__version__}")
|
40
|
+
|
41
|
+
CONF.log_opt_values(LOG, logging.DEBUG)
|
42
|
+
if not host:
|
43
|
+
host = CONF.admin.web_ip
|
44
|
+
if not port:
|
45
|
+
port = CONF.admin.web_port
|
46
|
+
|
47
|
+
msg = f"Fetching stats from {host}:{port}"
|
48
|
+
console.print(msg)
|
49
|
+
with console.status(msg):
|
50
|
+
response = requests.get(f"http://{host}:{port}/stats", timeout=120)
|
51
|
+
if not response:
|
52
|
+
console.print(
|
53
|
+
f"Failed to fetch stats from {host}:{port}?",
|
54
|
+
style="bold red",
|
55
|
+
)
|
56
|
+
return
|
57
|
+
|
58
|
+
stats = response.json()
|
59
|
+
if not stats:
|
60
|
+
console.print(
|
61
|
+
f"Failed to fetch stats from aprsd admin ui at {host}:{port}",
|
62
|
+
style="bold red",
|
63
|
+
)
|
64
|
+
return
|
65
|
+
|
66
|
+
aprsd_title = (
|
67
|
+
"APRSD "
|
68
|
+
f"[bold cyan]v{stats['APRSDStats']['version']}[/] "
|
69
|
+
f"Callsign [bold green]{stats['APRSDStats']['callsign']}[/] "
|
70
|
+
f"Uptime [bold yellow]{stats['APRSDStats']['uptime']}[/]"
|
71
|
+
)
|
72
|
+
|
73
|
+
console.rule(f"Stats from {host}:{port}")
|
74
|
+
console.print("\n\n")
|
75
|
+
console.rule(aprsd_title)
|
76
|
+
|
77
|
+
# Show the connection to APRS
|
78
|
+
# It can be a connection to an APRS-IS server or a local TNC via KISS or KISSTCP
|
79
|
+
if "aprs-is" in stats:
|
80
|
+
title = f"APRS-IS Connection {stats['APRSClientStats']['server_string']}"
|
81
|
+
table = Table(title=title)
|
82
|
+
table.add_column("Key")
|
83
|
+
table.add_column("Value")
|
84
|
+
for key, value in stats["APRSClientStats"].items():
|
85
|
+
table.add_row(key, value)
|
86
|
+
console.print(table)
|
87
|
+
|
88
|
+
threads_table = Table(title="Threads")
|
89
|
+
threads_table.add_column("Name")
|
90
|
+
threads_table.add_column("Alive?")
|
91
|
+
for name, alive in stats["APRSDThreadList"].items():
|
92
|
+
threads_table.add_row(name, str(alive))
|
93
|
+
|
94
|
+
console.print(threads_table)
|
95
|
+
|
96
|
+
packet_totals = Table(title="Packet Totals")
|
97
|
+
packet_totals.add_column("Key")
|
98
|
+
packet_totals.add_column("Value")
|
99
|
+
packet_totals.add_row("Total Received", str(stats["PacketList"]["rx"]))
|
100
|
+
packet_totals.add_row("Total Sent", str(stats["PacketList"]["tx"]))
|
101
|
+
console.print(packet_totals)
|
102
|
+
|
103
|
+
# Show each of the packet types
|
104
|
+
packets_table = Table(title="Packets By Type")
|
105
|
+
packets_table.add_column("Packet Type")
|
106
|
+
packets_table.add_column("TX")
|
107
|
+
packets_table.add_column("RX")
|
108
|
+
for key, value in stats["PacketList"]["packets"].items():
|
109
|
+
packets_table.add_row(key, str(value["tx"]), str(value["rx"]))
|
110
|
+
|
111
|
+
console.print(packets_table)
|
112
|
+
|
113
|
+
if "plugins" in stats:
|
114
|
+
count = len(stats["PluginManager"])
|
115
|
+
plugins_table = Table(title=f"Plugins ({count})")
|
116
|
+
plugins_table.add_column("Plugin")
|
117
|
+
plugins_table.add_column("Enabled")
|
118
|
+
plugins_table.add_column("Version")
|
119
|
+
plugins_table.add_column("TX")
|
120
|
+
plugins_table.add_column("RX")
|
121
|
+
plugins = stats["PluginManager"]
|
122
|
+
for key, value in plugins.items():
|
123
|
+
plugins_table.add_row(
|
124
|
+
key,
|
125
|
+
str(plugins[key]["enabled"]),
|
126
|
+
plugins[key]["version"],
|
127
|
+
str(plugins[key]["tx"]),
|
128
|
+
str(plugins[key]["rx"]),
|
129
|
+
)
|
130
|
+
|
131
|
+
console.print(plugins_table)
|
132
|
+
|
133
|
+
seen_list = stats.get("SeenList")
|
134
|
+
|
135
|
+
if seen_list:
|
136
|
+
count = len(seen_list)
|
137
|
+
seen_table = Table(title=f"Seen List ({count})")
|
138
|
+
seen_table.add_column("Callsign")
|
139
|
+
seen_table.add_column("Message Count")
|
140
|
+
seen_table.add_column("Last Heard")
|
141
|
+
for key, value in seen_list.items():
|
142
|
+
seen_table.add_row(key, str(value["count"]), value["last"])
|
143
|
+
|
144
|
+
console.print(seen_table)
|
145
|
+
|
146
|
+
watch_list = stats.get("WatchList")
|
147
|
+
|
148
|
+
if watch_list:
|
149
|
+
count = len(watch_list)
|
150
|
+
watch_table = Table(title=f"Watch List ({count})")
|
151
|
+
watch_table.add_column("Callsign")
|
152
|
+
watch_table.add_column("Last Heard")
|
153
|
+
for key, value in watch_list.items():
|
154
|
+
watch_table.add_row(key, value["last"])
|
155
|
+
|
156
|
+
console.print(watch_table)
|
@@ -0,0 +1,86 @@
|
|
1
|
+
#
|
2
|
+
# Used to fetch the stats url and determine if
|
3
|
+
# aprsd server is 'healthy'
|
4
|
+
#
|
5
|
+
#
|
6
|
+
# python included libs
|
7
|
+
import datetime
|
8
|
+
import logging
|
9
|
+
import sys
|
10
|
+
|
11
|
+
import click
|
12
|
+
from oslo_config import cfg
|
13
|
+
from rich.console import Console
|
14
|
+
|
15
|
+
import aprsd
|
16
|
+
from aprsd import cli_helper
|
17
|
+
from aprsd import conf # noqa
|
18
|
+
# local imports here
|
19
|
+
from aprsd.main import cli
|
20
|
+
from aprsd.threads import stats as stats_threads
|
21
|
+
|
22
|
+
|
23
|
+
# setup the global logger
|
24
|
+
# log.basicConfig(level=log.DEBUG) # level=10
|
25
|
+
CONF = cfg.CONF
|
26
|
+
LOG = logging.getLogger("APRSD")
|
27
|
+
console = Console()
|
28
|
+
|
29
|
+
|
30
|
+
@cli.command()
|
31
|
+
@cli_helper.add_options(cli_helper.common_options)
|
32
|
+
@click.option(
|
33
|
+
"--timeout",
|
34
|
+
show_default=True,
|
35
|
+
default=3,
|
36
|
+
help="How long to wait for healtcheck url to come back",
|
37
|
+
)
|
38
|
+
@click.pass_context
|
39
|
+
@cli_helper.process_standard_options
|
40
|
+
def healthcheck(ctx, timeout):
|
41
|
+
"""Check the health of the running aprsd server."""
|
42
|
+
ver_str = f"APRSD HealthCheck version: {aprsd.__version__}"
|
43
|
+
console.log(ver_str)
|
44
|
+
|
45
|
+
with console.status(ver_str):
|
46
|
+
try:
|
47
|
+
stats_obj = stats_threads.StatsStore()
|
48
|
+
stats_obj.load()
|
49
|
+
stats = stats_obj.data
|
50
|
+
# console.print(stats)
|
51
|
+
except Exception as ex:
|
52
|
+
console.log(f"Failed to load stats: '{ex}'")
|
53
|
+
sys.exit(-1)
|
54
|
+
else:
|
55
|
+
now = datetime.datetime.now()
|
56
|
+
if not stats:
|
57
|
+
console.log("No stats from aprsd")
|
58
|
+
sys.exit(-1)
|
59
|
+
|
60
|
+
email_stats = stats.get("EmailStats")
|
61
|
+
if email_stats:
|
62
|
+
email_thread_last_update = email_stats["last_check_time"]
|
63
|
+
|
64
|
+
if email_thread_last_update != "never":
|
65
|
+
d = now - email_thread_last_update
|
66
|
+
max_timeout = {"hours": 0.0, "minutes": 5, "seconds": 0}
|
67
|
+
max_delta = datetime.timedelta(**max_timeout)
|
68
|
+
if d > max_delta:
|
69
|
+
console.log(f"Email thread is very old! {d}")
|
70
|
+
sys.exit(-1)
|
71
|
+
|
72
|
+
client_stats = stats.get("APRSClientStats")
|
73
|
+
if not client_stats:
|
74
|
+
console.log("No APRSClientStats")
|
75
|
+
sys.exit(-1)
|
76
|
+
else:
|
77
|
+
aprsis_last_update = client_stats["server_keepalive"]
|
78
|
+
d = now - aprsis_last_update
|
79
|
+
max_timeout = {"hours": 0.0, "minutes": 5, "seconds": 0}
|
80
|
+
max_delta = datetime.timedelta(**max_timeout)
|
81
|
+
if d > max_delta:
|
82
|
+
LOG.error(f"APRS-IS last update is very old! {d}")
|
83
|
+
sys.exit(-1)
|
84
|
+
|
85
|
+
console.log("OK")
|
86
|
+
sys.exit(0)
|
@@ -0,0 +1,319 @@
|
|
1
|
+
import fnmatch
|
2
|
+
import importlib
|
3
|
+
import inspect
|
4
|
+
import logging
|
5
|
+
import os
|
6
|
+
import pkgutil
|
7
|
+
import re
|
8
|
+
import sys
|
9
|
+
from traceback import print_tb
|
10
|
+
from urllib.parse import urljoin
|
11
|
+
|
12
|
+
from bs4 import BeautifulSoup
|
13
|
+
import click
|
14
|
+
import requests
|
15
|
+
from rich.console import Console
|
16
|
+
from rich.table import Table
|
17
|
+
from rich.text import Text
|
18
|
+
from thesmuggler import smuggle
|
19
|
+
|
20
|
+
from aprsd import cli_helper
|
21
|
+
from aprsd import plugin as aprsd_plugin
|
22
|
+
from aprsd.main import cli
|
23
|
+
from aprsd.plugins import (
|
24
|
+
email, fortune, location, notify, ping, time, version, weather,
|
25
|
+
)
|
26
|
+
|
27
|
+
|
28
|
+
LOG = logging.getLogger("APRSD")
|
29
|
+
PYPI_URL = "https://pypi.org/search/"
|
30
|
+
|
31
|
+
|
32
|
+
def onerror(name):
|
33
|
+
print(f"Error importing module {name}")
|
34
|
+
type, value, traceback = sys.exc_info()
|
35
|
+
print_tb(traceback)
|
36
|
+
|
37
|
+
|
38
|
+
def is_plugin(obj):
|
39
|
+
for c in inspect.getmro(obj):
|
40
|
+
if issubclass(c, aprsd_plugin.APRSDPluginBase):
|
41
|
+
return True
|
42
|
+
|
43
|
+
return False
|
44
|
+
|
45
|
+
|
46
|
+
def plugin_type(obj):
|
47
|
+
for c in inspect.getmro(obj):
|
48
|
+
if issubclass(c, aprsd_plugin.APRSDRegexCommandPluginBase):
|
49
|
+
return "RegexCommand"
|
50
|
+
if issubclass(c, aprsd_plugin.APRSDWatchListPluginBase):
|
51
|
+
return "WatchList"
|
52
|
+
if issubclass(c, aprsd_plugin.APRSDPluginBase):
|
53
|
+
return "APRSDPluginBase"
|
54
|
+
|
55
|
+
return "Unknown"
|
56
|
+
|
57
|
+
|
58
|
+
def walk_package(package):
|
59
|
+
return pkgutil.walk_packages(
|
60
|
+
package.__path__,
|
61
|
+
package.__name__ + ".",
|
62
|
+
onerror=onerror,
|
63
|
+
)
|
64
|
+
|
65
|
+
|
66
|
+
def get_module_info(package_name, module_name, module_path):
|
67
|
+
if not os.path.exists(module_path):
|
68
|
+
return None
|
69
|
+
|
70
|
+
dir_path = os.path.realpath(module_path)
|
71
|
+
pattern = "*.py"
|
72
|
+
|
73
|
+
obj_list = []
|
74
|
+
|
75
|
+
for path, _subdirs, files in os.walk(dir_path):
|
76
|
+
for name in files:
|
77
|
+
if fnmatch.fnmatch(name, pattern):
|
78
|
+
module = smuggle(f"{path}/{name}")
|
79
|
+
for mem_name, obj in inspect.getmembers(module):
|
80
|
+
if inspect.isclass(obj) and is_plugin(obj):
|
81
|
+
obj_list.append(
|
82
|
+
{
|
83
|
+
"package": package_name,
|
84
|
+
"name": mem_name, "obj": obj,
|
85
|
+
"version": obj.version,
|
86
|
+
"path": f"{'.'.join([module_name, obj.__name__])}",
|
87
|
+
},
|
88
|
+
)
|
89
|
+
|
90
|
+
return obj_list
|
91
|
+
|
92
|
+
|
93
|
+
def _get_installed_aprsd_items():
|
94
|
+
# installed plugins
|
95
|
+
plugins = {}
|
96
|
+
extensions = {}
|
97
|
+
for finder, name, ispkg in pkgutil.iter_modules():
|
98
|
+
if name.startswith("aprsd_"):
|
99
|
+
print(f"Found aprsd_ module: {name}")
|
100
|
+
if ispkg:
|
101
|
+
module = importlib.import_module(name)
|
102
|
+
pkgs = walk_package(module)
|
103
|
+
for pkg in pkgs:
|
104
|
+
pkg_info = get_module_info(module.__name__, pkg.name, module.__path__[0])
|
105
|
+
if "plugin" in name:
|
106
|
+
plugins[name] = pkg_info
|
107
|
+
elif "extension" in name:
|
108
|
+
extensions[name] = pkg_info
|
109
|
+
return plugins, extensions
|
110
|
+
|
111
|
+
|
112
|
+
def get_installed_plugins():
|
113
|
+
# installed plugins
|
114
|
+
plugins, extensions = _get_installed_aprsd_items()
|
115
|
+
return plugins
|
116
|
+
|
117
|
+
|
118
|
+
def get_installed_extensions():
|
119
|
+
# installed plugins
|
120
|
+
plugins, extensions = _get_installed_aprsd_items()
|
121
|
+
return extensions
|
122
|
+
|
123
|
+
|
124
|
+
def show_built_in_plugins(console):
|
125
|
+
modules = [email, fortune, location, notify, ping, time, version, weather]
|
126
|
+
plugins = []
|
127
|
+
|
128
|
+
for module in modules:
|
129
|
+
entries = inspect.getmembers(module, inspect.isclass)
|
130
|
+
for entry in entries:
|
131
|
+
cls = entry[1]
|
132
|
+
if issubclass(cls, aprsd_plugin.APRSDPluginBase):
|
133
|
+
info = {
|
134
|
+
"name": cls.__qualname__,
|
135
|
+
"path": f"{cls.__module__}.{cls.__qualname__}",
|
136
|
+
"version": cls.version,
|
137
|
+
"docstring": cls.__doc__,
|
138
|
+
"short_desc": cls.short_description,
|
139
|
+
}
|
140
|
+
|
141
|
+
if issubclass(cls, aprsd_plugin.APRSDRegexCommandPluginBase):
|
142
|
+
info["command_regex"] = cls.command_regex
|
143
|
+
info["type"] = "RegexCommand"
|
144
|
+
|
145
|
+
if issubclass(cls, aprsd_plugin.APRSDWatchListPluginBase):
|
146
|
+
info["type"] = "WatchList"
|
147
|
+
|
148
|
+
plugins.append(info)
|
149
|
+
|
150
|
+
plugins = sorted(plugins, key=lambda i: i["name"])
|
151
|
+
|
152
|
+
table = Table(
|
153
|
+
title="[not italic]:snake:[/] [bold][magenta]APRSD Built-in Plugins [not italic]:snake:[/]",
|
154
|
+
)
|
155
|
+
table.add_column("Plugin Name", style="cyan", no_wrap=True)
|
156
|
+
table.add_column("Info", style="bold yellow")
|
157
|
+
table.add_column("Type", style="bold green")
|
158
|
+
table.add_column("Plugin Path", style="bold blue")
|
159
|
+
for entry in plugins:
|
160
|
+
table.add_row(entry["name"], entry["short_desc"], entry["type"], entry["path"])
|
161
|
+
|
162
|
+
console.print(table)
|
163
|
+
|
164
|
+
|
165
|
+
def _get_pypi_packages():
|
166
|
+
query = "aprsd"
|
167
|
+
snippets = []
|
168
|
+
s = requests.Session()
|
169
|
+
for page in range(1, 3):
|
170
|
+
params = {"q": query, "page": page}
|
171
|
+
r = s.get(PYPI_URL, params=params)
|
172
|
+
soup = BeautifulSoup(r.text, "html.parser")
|
173
|
+
snippets += soup.select('a[class*="snippet"]')
|
174
|
+
if not hasattr(s, "start_url"):
|
175
|
+
s.start_url = r.url.rsplit("&page", maxsplit=1).pop(0)
|
176
|
+
|
177
|
+
return snippets
|
178
|
+
|
179
|
+
|
180
|
+
def show_pypi_plugins(installed_plugins, console):
|
181
|
+
snippets = _get_pypi_packages()
|
182
|
+
|
183
|
+
title = Text.assemble(
|
184
|
+
("Pypi.org APRSD Installable Plugin Packages\n\n", "bold magenta"),
|
185
|
+
("Install any of the following plugins with\n", "bold yellow"),
|
186
|
+
("'pip install ", "bold white"),
|
187
|
+
("<Plugin Package Name>'", "cyan"),
|
188
|
+
)
|
189
|
+
|
190
|
+
table = Table(title=title)
|
191
|
+
table.add_column("Plugin Package Name", style="cyan", no_wrap=True)
|
192
|
+
table.add_column("Description", style="yellow")
|
193
|
+
table.add_column("Version", style="yellow", justify="center")
|
194
|
+
table.add_column("Released", style="bold green", justify="center")
|
195
|
+
table.add_column("Installed?", style="red", justify="center")
|
196
|
+
for snippet in snippets:
|
197
|
+
link = urljoin(PYPI_URL, snippet.get("href"))
|
198
|
+
package = re.sub(r"\s+", " ", snippet.select_one('span[class*="name"]').text.strip())
|
199
|
+
version = re.sub(r"\s+", " ", snippet.select_one('span[class*="version"]').text.strip())
|
200
|
+
created = re.sub(r"\s+", " ", snippet.select_one('span[class*="created"]').text.strip())
|
201
|
+
description = re.sub(r"\s+", " ", snippet.select_one('p[class*="description"]').text.strip())
|
202
|
+
emoji = ":open_file_folder:"
|
203
|
+
|
204
|
+
if "aprsd-" not in package or "-plugin" not in package:
|
205
|
+
continue
|
206
|
+
|
207
|
+
under = package.replace("-", "_")
|
208
|
+
if under in installed_plugins:
|
209
|
+
installed = "Yes"
|
210
|
+
else:
|
211
|
+
installed = "No"
|
212
|
+
|
213
|
+
table.add_row(
|
214
|
+
f"[link={link}]{emoji}[/link] {package}",
|
215
|
+
description, version, created, installed,
|
216
|
+
)
|
217
|
+
|
218
|
+
console.print("\n")
|
219
|
+
console.print(table)
|
220
|
+
|
221
|
+
|
222
|
+
def show_pypi_extensions(installed_extensions, console):
|
223
|
+
snippets = _get_pypi_packages()
|
224
|
+
|
225
|
+
title = Text.assemble(
|
226
|
+
("Pypi.org APRSD Installable Extension Packages\n\n", "bold magenta"),
|
227
|
+
("Install any of the following extensions by running\n", "bold yellow"),
|
228
|
+
("'pip install ", "bold white"),
|
229
|
+
("<Plugin Package Name>'", "cyan"),
|
230
|
+
)
|
231
|
+
table = Table(title=title)
|
232
|
+
table.add_column("Extension Package Name", style="cyan", no_wrap=True)
|
233
|
+
table.add_column("Description", style="yellow")
|
234
|
+
table.add_column("Version", style="yellow", justify="center")
|
235
|
+
table.add_column("Released", style="bold green", justify="center")
|
236
|
+
table.add_column("Installed?", style="red", justify="center")
|
237
|
+
for snippet in snippets:
|
238
|
+
link = urljoin(PYPI_URL, snippet.get("href"))
|
239
|
+
package = re.sub(r"\s+", " ", snippet.select_one('span[class*="name"]').text.strip())
|
240
|
+
version = re.sub(r"\s+", " ", snippet.select_one('span[class*="version"]').text.strip())
|
241
|
+
created = re.sub(r"\s+", " ", snippet.select_one('span[class*="created"]').text.strip())
|
242
|
+
description = re.sub(r"\s+", " ", snippet.select_one('p[class*="description"]').text.strip())
|
243
|
+
emoji = ":open_file_folder:"
|
244
|
+
|
245
|
+
if "aprsd-" not in package or "-extension" not in package:
|
246
|
+
continue
|
247
|
+
|
248
|
+
under = package.replace("-", "_")
|
249
|
+
if under in installed_extensions:
|
250
|
+
installed = "Yes"
|
251
|
+
else:
|
252
|
+
installed = "No"
|
253
|
+
|
254
|
+
table.add_row(
|
255
|
+
f"[link={link}]{emoji}[/link] {package}",
|
256
|
+
description, version, created, installed,
|
257
|
+
)
|
258
|
+
|
259
|
+
console.print("\n")
|
260
|
+
console.print(table)
|
261
|
+
|
262
|
+
|
263
|
+
def show_installed_plugins(installed_plugins, console):
|
264
|
+
if not installed_plugins:
|
265
|
+
return
|
266
|
+
|
267
|
+
table = Table(
|
268
|
+
title="[not italic]:snake:[/] [bold][magenta]APRSD Installed 3rd party Plugins [not italic]:snake:[/]",
|
269
|
+
)
|
270
|
+
table.add_column("Package Name", style=" bold white", no_wrap=True)
|
271
|
+
table.add_column("Plugin Name", style="cyan", no_wrap=True)
|
272
|
+
table.add_column("Version", style="yellow", justify="center")
|
273
|
+
table.add_column("Type", style="bold green")
|
274
|
+
table.add_column("Plugin Path", style="bold blue")
|
275
|
+
for name in installed_plugins:
|
276
|
+
for plugin in installed_plugins[name]:
|
277
|
+
table.add_row(
|
278
|
+
name.replace("_", "-"),
|
279
|
+
plugin["name"],
|
280
|
+
plugin["version"],
|
281
|
+
plugin_type(plugin["obj"]),
|
282
|
+
plugin["path"],
|
283
|
+
)
|
284
|
+
|
285
|
+
console.print("\n")
|
286
|
+
console.print(table)
|
287
|
+
|
288
|
+
|
289
|
+
@cli.command()
|
290
|
+
@cli_helper.add_options(cli_helper.common_options)
|
291
|
+
@click.pass_context
|
292
|
+
@cli_helper.process_standard_options_no_config
|
293
|
+
def list_plugins(ctx):
|
294
|
+
"""List the built in plugins available to APRSD."""
|
295
|
+
console = Console()
|
296
|
+
|
297
|
+
with console.status("Show Built-in Plugins") as status:
|
298
|
+
show_built_in_plugins(console)
|
299
|
+
|
300
|
+
status.update("Fetching pypi.org plugins")
|
301
|
+
installed_plugins = get_installed_plugins()
|
302
|
+
show_pypi_plugins(installed_plugins, console)
|
303
|
+
|
304
|
+
status.update("Looking for installed APRSD plugins")
|
305
|
+
show_installed_plugins(installed_plugins, console)
|
306
|
+
|
307
|
+
|
308
|
+
@cli.command()
|
309
|
+
@cli_helper.add_options(cli_helper.common_options)
|
310
|
+
@click.pass_context
|
311
|
+
@cli_helper.process_standard_options_no_config
|
312
|
+
def list_extensions(ctx):
|
313
|
+
"""List the built in plugins available to APRSD."""
|
314
|
+
console = Console()
|
315
|
+
|
316
|
+
with console.status("Show APRSD Extensions") as status:
|
317
|
+
status.update("Fetching pypi.org APRSD Extensions")
|
318
|
+
installed_extensions = get_installed_extensions()
|
319
|
+
show_pypi_extensions(installed_extensions, console)
|