aprsd 3.4.3__py3-none-any.whl → 4.0.0__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/cli_helper.py +12 -5
- aprsd/client/aprsis.py +68 -17
- aprsd/client/base.py +60 -12
- aprsd/client/drivers/aprsis.py +11 -5
- aprsd/client/drivers/fake.py +15 -20
- aprsd/client/factory.py +6 -3
- aprsd/client/fake.py +5 -4
- aprsd/client/kiss.py +43 -7
- aprsd/client/stats.py +2 -22
- aprsd/cmds/completion.py +7 -4
- aprsd/cmds/dev.py +39 -43
- aprsd/cmds/fetch_stats.py +221 -69
- aprsd/cmds/healthcheck.py +7 -5
- aprsd/cmds/list_plugins.py +140 -134
- aprsd/cmds/listen.py +102 -11
- aprsd/cmds/server.py +71 -37
- aprsd/conf/__init__.py +1 -2
- aprsd/conf/client.py +3 -4
- aprsd/conf/common.py +29 -92
- aprsd/conf/log.py +2 -4
- aprsd/conf/opts.py +5 -4
- aprsd/conf/plugin_common.py +11 -121
- aprsd/exception.py +2 -0
- aprsd/log/log.py +7 -46
- aprsd/main.py +19 -9
- aprsd/packets/__init__.py +14 -4
- aprsd/packets/core.py +82 -91
- aprsd/packets/log.py +8 -8
- aprsd/packets/packet_list.py +18 -25
- aprsd/plugin.py +33 -15
- aprsd/plugins/fortune.py +2 -2
- aprsd/plugins/notify.py +1 -4
- aprsd/plugins/weather.py +10 -8
- aprsd/stats/__init__.py +0 -2
- aprsd/stats/collector.py +11 -3
- aprsd/threads/__init__.py +3 -2
- aprsd/threads/aprsd.py +57 -10
- aprsd/threads/{keep_alive.py → keepalive.py} +14 -37
- aprsd/threads/registry.py +3 -3
- aprsd/threads/rx.py +48 -17
- aprsd/threads/stats.py +2 -2
- aprsd/threads/tx.py +34 -10
- aprsd/utils/__init__.py +62 -15
- aprsd/utils/counter.py +15 -12
- aprsd/utils/json.py +9 -4
- aprsd/utils/keepalive_collector.py +55 -0
- aprsd/utils/trace.py +4 -4
- aprsd-4.0.0.dist-info/AUTHORS +1 -0
- aprsd-4.0.0.dist-info/METADATA +293 -0
- aprsd-4.0.0.dist-info/RECORD +74 -0
- {aprsd-3.4.3.dist-info → aprsd-4.0.0.dist-info}/WHEEL +1 -1
- aprsd/cmds/webchat.py +0 -674
- aprsd/conf/plugin_email.py +0 -105
- aprsd/plugins/email.py +0 -709
- aprsd/plugins/location.py +0 -179
- aprsd/threads/log_monitor.py +0 -121
- aprsd/web/__init__.py +0 -0
- aprsd/web/admin/__init__.py +0 -0
- aprsd/web/admin/static/css/index.css +0 -84
- aprsd/web/admin/static/css/prism.css +0 -4
- aprsd/web/admin/static/css/tabs.css +0 -35
- 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 +0 -235
- aprsd/web/admin/static/js/echarts.js +0 -465
- aprsd/web/admin/static/js/logs.js +0 -26
- aprsd/web/admin/static/js/main.js +0 -231
- aprsd/web/admin/static/js/prism.js +0 -12
- aprsd/web/admin/static/js/send-message.js +0 -114
- aprsd/web/admin/static/js/tabs.js +0 -28
- aprsd/web/admin/templates/index.html +0 -196
- aprsd/web/chat/static/css/chat.css +0 -115
- aprsd/web/chat/static/css/index.css +0 -66
- aprsd/web/chat/static/css/style.css.map +0 -1
- aprsd/web/chat/static/css/tabs.css +0 -41
- aprsd/web/chat/static/css/upstream/bootstrap.min.css +0 -6
- aprsd/web/chat/static/css/upstream/font.woff2 +0 -0
- aprsd/web/chat/static/css/upstream/google-fonts.css +0 -23
- aprsd/web/chat/static/css/upstream/jquery-ui.css +0 -1311
- aprsd/web/chat/static/css/upstream/jquery.toast.css +0 -28
- 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 +0 -3
- aprsd/web/chat/static/js/gps.js +0 -84
- aprsd/web/chat/static/js/main.js +0 -45
- aprsd/web/chat/static/js/send-message.js +0 -585
- aprsd/web/chat/static/js/tabs.js +0 -28
- aprsd/web/chat/static/js/upstream/bootstrap.bundle.min.js +0 -7
- aprsd/web/chat/static/js/upstream/jquery-3.7.1.min.js +0 -2
- aprsd/web/chat/static/js/upstream/jquery-ui.min.js +0 -13
- aprsd/web/chat/static/js/upstream/jquery.toast.js +0 -374
- aprsd/web/chat/static/js/upstream/semantic.min.js +0 -11
- aprsd/web/chat/static/js/upstream/socket.io.min.js +0 -7
- aprsd/web/chat/templates/index.html +0 -139
- aprsd/wsgi.py +0 -315
- aprsd-3.4.3.dist-info/AUTHORS +0 -13
- aprsd-3.4.3.dist-info/METADATA +0 -793
- aprsd-3.4.3.dist-info/RECORD +0 -133
- {aprsd-3.4.3.dist-info → aprsd-4.0.0.dist-info}/LICENSE +0 -0
- {aprsd-3.4.3.dist-info → aprsd-4.0.0.dist-info}/entry_points.txt +0 -0
- {aprsd-3.4.3.dist-info → aprsd-4.0.0.dist-info}/top_level.txt +0 -0
aprsd/cmds/list_plugins.py
CHANGED
@@ -4,12 +4,9 @@ import inspect
|
|
4
4
|
import logging
|
5
5
|
import os
|
6
6
|
import pkgutil
|
7
|
-
import re
|
8
7
|
import sys
|
9
8
|
from traceback import print_tb
|
10
|
-
from urllib.parse import urljoin
|
11
9
|
|
12
|
-
from bs4 import BeautifulSoup
|
13
10
|
import click
|
14
11
|
import requests
|
15
12
|
from rich.console import Console
|
@@ -20,17 +17,14 @@ from thesmuggler import smuggle
|
|
20
17
|
from aprsd import cli_helper
|
21
18
|
from aprsd import plugin as aprsd_plugin
|
22
19
|
from aprsd.main import cli
|
23
|
-
from aprsd.plugins import
|
24
|
-
email, fortune, location, notify, ping, time, version, weather,
|
25
|
-
)
|
20
|
+
from aprsd.plugins import fortune, notify, ping, time, version, weather
|
26
21
|
|
27
|
-
|
28
|
-
|
29
|
-
PYPI_URL = "https://pypi.org/search/"
|
22
|
+
LOG = logging.getLogger('APRSD')
|
23
|
+
PYPI_URL = 'https://pypi.org/search/'
|
30
24
|
|
31
25
|
|
32
26
|
def onerror(name):
|
33
|
-
print(f
|
27
|
+
print(f'Error importing module {name}')
|
34
28
|
type, value, traceback = sys.exc_info()
|
35
29
|
print_tb(traceback)
|
36
30
|
|
@@ -46,19 +40,19 @@ def is_plugin(obj):
|
|
46
40
|
def plugin_type(obj):
|
47
41
|
for c in inspect.getmro(obj):
|
48
42
|
if issubclass(c, aprsd_plugin.APRSDRegexCommandPluginBase):
|
49
|
-
return
|
43
|
+
return 'RegexCommand'
|
50
44
|
if issubclass(c, aprsd_plugin.APRSDWatchListPluginBase):
|
51
|
-
return
|
45
|
+
return 'WatchList'
|
52
46
|
if issubclass(c, aprsd_plugin.APRSDPluginBase):
|
53
|
-
return
|
47
|
+
return 'APRSDPluginBase'
|
54
48
|
|
55
|
-
return
|
49
|
+
return 'Unknown'
|
56
50
|
|
57
51
|
|
58
52
|
def walk_package(package):
|
59
53
|
return pkgutil.walk_packages(
|
60
54
|
package.__path__,
|
61
|
-
package.__name__ +
|
55
|
+
package.__name__ + '.',
|
62
56
|
onerror=onerror,
|
63
57
|
)
|
64
58
|
|
@@ -68,22 +62,23 @@ def get_module_info(package_name, module_name, module_path):
|
|
68
62
|
return None
|
69
63
|
|
70
64
|
dir_path = os.path.realpath(module_path)
|
71
|
-
pattern =
|
65
|
+
pattern = '*.py'
|
72
66
|
|
73
67
|
obj_list = []
|
74
68
|
|
75
69
|
for path, _subdirs, files in os.walk(dir_path):
|
76
70
|
for name in files:
|
77
71
|
if fnmatch.fnmatch(name, pattern):
|
78
|
-
module = smuggle(f
|
72
|
+
module = smuggle(f'{path}/{name}')
|
79
73
|
for mem_name, obj in inspect.getmembers(module):
|
80
74
|
if inspect.isclass(obj) and is_plugin(obj):
|
81
75
|
obj_list.append(
|
82
76
|
{
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
77
|
+
'package': package_name,
|
78
|
+
'name': mem_name,
|
79
|
+
'obj': obj,
|
80
|
+
'version': obj.version,
|
81
|
+
'path': f'{".".join([module_name, obj.__name__])}',
|
87
82
|
},
|
88
83
|
)
|
89
84
|
|
@@ -94,18 +89,18 @@ def _get_installed_aprsd_items():
|
|
94
89
|
# installed plugins
|
95
90
|
plugins = {}
|
96
91
|
extensions = {}
|
97
|
-
for
|
98
|
-
if name.startswith(
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
92
|
+
for _finder, name, ispkg in pkgutil.iter_modules():
|
93
|
+
if ispkg and name.startswith('aprsd_'):
|
94
|
+
module = importlib.import_module(name)
|
95
|
+
pkgs = walk_package(module)
|
96
|
+
for pkg in pkgs:
|
97
|
+
pkg_info = get_module_info(
|
98
|
+
module.__name__, pkg.name, module.__path__[0]
|
99
|
+
)
|
100
|
+
if 'plugin' in name:
|
101
|
+
plugins[name] = pkg_info
|
102
|
+
elif 'extension' in name:
|
103
|
+
extensions[name] = pkg_info
|
109
104
|
return plugins, extensions
|
110
105
|
|
111
106
|
|
@@ -122,7 +117,7 @@ def get_installed_extensions():
|
|
122
117
|
|
123
118
|
|
124
119
|
def show_built_in_plugins(console):
|
125
|
-
modules = [
|
120
|
+
modules = [fortune, notify, ping, time, version, weather]
|
126
121
|
plugins = []
|
127
122
|
|
128
123
|
for module in modules:
|
@@ -131,132 +126,141 @@ def show_built_in_plugins(console):
|
|
131
126
|
cls = entry[1]
|
132
127
|
if issubclass(cls, aprsd_plugin.APRSDPluginBase):
|
133
128
|
info = {
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
129
|
+
'name': cls.__qualname__,
|
130
|
+
'path': f'{cls.__module__}.{cls.__qualname__}',
|
131
|
+
'version': cls.version,
|
132
|
+
'docstring': cls.__doc__,
|
133
|
+
'short_desc': cls.short_description,
|
139
134
|
}
|
140
135
|
|
141
136
|
if issubclass(cls, aprsd_plugin.APRSDRegexCommandPluginBase):
|
142
|
-
info[
|
143
|
-
info[
|
137
|
+
info['command_regex'] = cls.command_regex
|
138
|
+
info['type'] = 'RegexCommand'
|
144
139
|
|
145
140
|
if issubclass(cls, aprsd_plugin.APRSDWatchListPluginBase):
|
146
|
-
info[
|
141
|
+
info['type'] = 'WatchList'
|
147
142
|
|
148
143
|
plugins.append(info)
|
149
144
|
|
150
|
-
plugins = sorted(plugins, key=lambda i: i[
|
145
|
+
plugins = sorted(plugins, key=lambda i: i['name'])
|
151
146
|
|
152
147
|
table = Table(
|
153
|
-
title=
|
148
|
+
title='[not italic]:snake:[/] [bold][magenta]APRSD Built-in Plugins [not italic]:snake:[/]',
|
154
149
|
)
|
155
|
-
table.add_column(
|
156
|
-
table.add_column(
|
157
|
-
table.add_column(
|
158
|
-
table.add_column(
|
150
|
+
table.add_column('Plugin Name', style='cyan', no_wrap=True)
|
151
|
+
table.add_column('Info', style='bold yellow')
|
152
|
+
table.add_column('Type', style='bold green')
|
153
|
+
table.add_column('Plugin Path', style='bold blue')
|
159
154
|
for entry in plugins:
|
160
|
-
table.add_row(entry[
|
155
|
+
table.add_row(entry['name'], entry['short_desc'], entry['type'], entry['path'])
|
161
156
|
|
162
157
|
console.print(table)
|
163
158
|
|
164
159
|
|
165
160
|
def _get_pypi_packages():
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
161
|
+
if simple_r := requests.get(
|
162
|
+
'https://pypi.org/simple',
|
163
|
+
headers={'Accept': 'application/vnd.pypi.simple.v1+json'},
|
164
|
+
):
|
165
|
+
simple_response = simple_r.json()
|
166
|
+
else:
|
167
|
+
simple_response = {}
|
168
|
+
|
169
|
+
key = 'aprsd'
|
170
|
+
matches = [
|
171
|
+
p['name'] for p in simple_response['projects'] if p['name'].startswith(key)
|
172
|
+
]
|
173
|
+
|
174
|
+
packages = []
|
175
|
+
for pkg in matches:
|
176
|
+
# Get info for first match
|
177
|
+
if r := requests.get(
|
178
|
+
f'https://pypi.org/pypi/{pkg}/json',
|
179
|
+
headers={'Accept': 'application/json'},
|
180
|
+
):
|
181
|
+
packages.append(r.json())
|
182
|
+
|
183
|
+
return packages
|
178
184
|
|
179
185
|
|
180
186
|
def show_pypi_plugins(installed_plugins, console):
|
181
|
-
|
187
|
+
packages = _get_pypi_packages()
|
182
188
|
|
183
189
|
title = Text.assemble(
|
184
|
-
(
|
185
|
-
(
|
186
|
-
("'pip install ",
|
187
|
-
("<Plugin Package Name>'",
|
190
|
+
('Pypi.org APRSD Installable Plugin Packages\n\n', 'bold magenta'),
|
191
|
+
('Install any of the following plugins with\n', 'bold yellow'),
|
192
|
+
("'pip install ", 'bold white'),
|
193
|
+
("<Plugin Package Name>'", 'cyan'),
|
188
194
|
)
|
189
195
|
|
190
196
|
table = Table(title=title)
|
191
|
-
table.add_column(
|
192
|
-
table.add_column(
|
193
|
-
table.add_column(
|
194
|
-
table.add_column(
|
195
|
-
table.add_column(
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
version =
|
200
|
-
|
201
|
-
description =
|
202
|
-
|
203
|
-
|
204
|
-
if
|
197
|
+
table.add_column('Plugin Package Name', style='cyan', no_wrap=True)
|
198
|
+
table.add_column('Description', style='yellow')
|
199
|
+
table.add_column('Version', style='yellow', justify='center')
|
200
|
+
table.add_column('Released', style='bold green', justify='center')
|
201
|
+
table.add_column('Installed?', style='red', justify='center')
|
202
|
+
emoji = ':open_file_folder:'
|
203
|
+
for package in packages:
|
204
|
+
link = package['info']['package_url']
|
205
|
+
version = package['info']['version']
|
206
|
+
package_name = package['info']['name']
|
207
|
+
description = package['info']['summary']
|
208
|
+
created = package['releases'][version][0]['upload_time']
|
209
|
+
|
210
|
+
if 'aprsd-' not in package_name or '-plugin' not in package_name:
|
205
211
|
continue
|
206
212
|
|
207
|
-
under =
|
208
|
-
if under in installed_plugins
|
209
|
-
installed = "Yes"
|
210
|
-
else:
|
211
|
-
installed = "No"
|
212
|
-
|
213
|
+
under = package_name.replace('-', '_')
|
214
|
+
installed = 'Yes' if under in installed_plugins else 'No'
|
213
215
|
table.add_row(
|
214
|
-
f
|
215
|
-
description,
|
216
|
+
f'[link={link}]{emoji}[/link] {package_name}',
|
217
|
+
description,
|
218
|
+
version,
|
219
|
+
created,
|
220
|
+
installed,
|
216
221
|
)
|
217
222
|
|
218
|
-
console.print(
|
223
|
+
console.print('\n')
|
219
224
|
console.print(table)
|
220
225
|
|
221
226
|
|
222
227
|
def show_pypi_extensions(installed_extensions, console):
|
223
|
-
|
228
|
+
packages = _get_pypi_packages()
|
224
229
|
|
225
230
|
title = Text.assemble(
|
226
|
-
(
|
227
|
-
(
|
228
|
-
("'pip install ",
|
229
|
-
("<Plugin Package Name>'",
|
231
|
+
('Pypi.org APRSD Installable Extension Packages\n\n', 'bold magenta'),
|
232
|
+
('Install any of the following extensions by running\n', 'bold yellow'),
|
233
|
+
("'pip install ", 'bold white'),
|
234
|
+
("<Plugin Package Name>'", 'cyan'),
|
230
235
|
)
|
231
236
|
table = Table(title=title)
|
232
|
-
table.add_column(
|
233
|
-
table.add_column(
|
234
|
-
table.add_column(
|
235
|
-
table.add_column(
|
236
|
-
table.add_column(
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
if
|
237
|
+
table.add_column('Extension Package Name', style='cyan', no_wrap=True)
|
238
|
+
table.add_column('Description', style='yellow')
|
239
|
+
table.add_column('Version', style='yellow', justify='center')
|
240
|
+
table.add_column('Released', style='bold green', justify='center')
|
241
|
+
table.add_column('Installed?', style='red', justify='center')
|
242
|
+
emoji = ':open_file_folder:'
|
243
|
+
|
244
|
+
for package in packages:
|
245
|
+
link = package['info']['package_url']
|
246
|
+
version = package['info']['version']
|
247
|
+
package_name = package['info']['name']
|
248
|
+
description = package['info']['summary']
|
249
|
+
created = package['releases'][version][0]['upload_time']
|
250
|
+
if 'aprsd-' not in package_name or '-extension' not in package_name:
|
246
251
|
continue
|
247
252
|
|
248
|
-
under =
|
249
|
-
if under in installed_extensions
|
250
|
-
installed = "Yes"
|
251
|
-
else:
|
252
|
-
installed = "No"
|
253
|
-
|
253
|
+
under = package_name.replace('-', '_')
|
254
|
+
installed = 'Yes' if under in installed_extensions else 'No'
|
254
255
|
table.add_row(
|
255
|
-
f
|
256
|
-
description,
|
256
|
+
f'[link={link}]{emoji}[/link] {package_name}',
|
257
|
+
description,
|
258
|
+
version,
|
259
|
+
created,
|
260
|
+
installed,
|
257
261
|
)
|
258
262
|
|
259
|
-
console.print(
|
263
|
+
console.print('\n')
|
260
264
|
console.print(table)
|
261
265
|
|
262
266
|
|
@@ -265,24 +269,24 @@ def show_installed_plugins(installed_plugins, console):
|
|
265
269
|
return
|
266
270
|
|
267
271
|
table = Table(
|
268
|
-
title=
|
272
|
+
title='[not italic]:snake:[/] [bold][magenta]APRSD Installed 3rd party Plugins [not italic]:snake:[/]',
|
269
273
|
)
|
270
|
-
table.add_column(
|
271
|
-
table.add_column(
|
272
|
-
table.add_column(
|
273
|
-
table.add_column(
|
274
|
-
table.add_column(
|
274
|
+
table.add_column('Package Name', style=' bold white', no_wrap=True)
|
275
|
+
table.add_column('Plugin Name', style='cyan', no_wrap=True)
|
276
|
+
table.add_column('Version', style='yellow', justify='center')
|
277
|
+
table.add_column('Type', style='bold green')
|
278
|
+
table.add_column('Plugin Path', style='bold blue')
|
275
279
|
for name in installed_plugins:
|
276
280
|
for plugin in installed_plugins[name]:
|
277
281
|
table.add_row(
|
278
|
-
name.replace(
|
279
|
-
plugin[
|
280
|
-
plugin[
|
281
|
-
plugin_type(plugin[
|
282
|
-
plugin[
|
282
|
+
name.replace('_', '-'),
|
283
|
+
plugin['name'],
|
284
|
+
plugin['version'],
|
285
|
+
plugin_type(plugin['obj']),
|
286
|
+
plugin['path'],
|
283
287
|
)
|
284
288
|
|
285
|
-
console.print(
|
289
|
+
console.print('\n')
|
286
290
|
console.print(table)
|
287
291
|
|
288
292
|
|
@@ -294,14 +298,14 @@ def list_plugins(ctx):
|
|
294
298
|
"""List the built in plugins available to APRSD."""
|
295
299
|
console = Console()
|
296
300
|
|
297
|
-
with console.status(
|
301
|
+
with console.status('Show Built-in Plugins') as status:
|
298
302
|
show_built_in_plugins(console)
|
299
303
|
|
300
|
-
status.update(
|
304
|
+
status.update('Fetching pypi.org plugins')
|
301
305
|
installed_plugins = get_installed_plugins()
|
302
306
|
show_pypi_plugins(installed_plugins, console)
|
303
307
|
|
304
|
-
status.update(
|
308
|
+
status.update('Looking for installed APRSD plugins')
|
305
309
|
show_installed_plugins(installed_plugins, console)
|
306
310
|
|
307
311
|
|
@@ -313,7 +317,9 @@ def list_extensions(ctx):
|
|
313
317
|
"""List the built in plugins available to APRSD."""
|
314
318
|
console = Console()
|
315
319
|
|
316
|
-
with console.status(
|
317
|
-
status.update(
|
320
|
+
with console.status('Show APRSD Extensions') as status:
|
321
|
+
status.update('Fetching pypi.org APRSD Extensions')
|
322
|
+
|
323
|
+
status.update('Looking for installed APRSD Extensions')
|
318
324
|
installed_extensions = get_installed_extensions()
|
319
325
|
show_pypi_extensions(installed_extensions, console)
|
aprsd/cmds/listen.py
CHANGED
@@ -10,26 +10,28 @@ import sys
|
|
10
10
|
import time
|
11
11
|
|
12
12
|
import click
|
13
|
+
from loguru import logger
|
13
14
|
from oslo_config import cfg
|
14
15
|
from rich.console import Console
|
15
16
|
|
16
17
|
# local imports here
|
17
18
|
import aprsd
|
18
|
-
from aprsd import cli_helper, packets, plugin, threads
|
19
|
+
from aprsd import cli_helper, packets, plugin, threads, utils
|
19
20
|
from aprsd.client import client_factory
|
20
21
|
from aprsd.main import cli
|
21
22
|
from aprsd.packets import collector as packet_collector
|
22
23
|
from aprsd.packets import log as packet_log
|
23
24
|
from aprsd.packets import seen_list
|
24
25
|
from aprsd.stats import collector
|
25
|
-
from aprsd.threads import
|
26
|
+
from aprsd.threads import keepalive, rx
|
26
27
|
from aprsd.threads import stats as stats_thread
|
27
|
-
|
28
|
+
from aprsd.threads.aprsd import APRSDThread
|
28
29
|
|
29
30
|
# setup the global logger
|
30
31
|
# log.basicConfig(level=log.DEBUG) # level=10
|
31
32
|
LOG = logging.getLogger("APRSD")
|
32
33
|
CONF = cfg.CONF
|
34
|
+
LOGU = logger
|
33
35
|
console = Console()
|
34
36
|
|
35
37
|
|
@@ -47,12 +49,20 @@ def signal_handler(sig, frame):
|
|
47
49
|
|
48
50
|
|
49
51
|
class APRSDListenThread(rx.APRSDRXThread):
|
50
|
-
def __init__(
|
52
|
+
def __init__(
|
53
|
+
self,
|
54
|
+
packet_queue,
|
55
|
+
packet_filter=None,
|
56
|
+
plugin_manager=None,
|
57
|
+
enabled_plugins=[],
|
58
|
+
log_packets=False,
|
59
|
+
):
|
51
60
|
super().__init__(packet_queue)
|
52
61
|
self.packet_filter = packet_filter
|
53
62
|
self.plugin_manager = plugin_manager
|
54
63
|
if self.plugin_manager:
|
55
64
|
LOG.info(f"Plugins {self.plugin_manager.get_message_plugins()}")
|
65
|
+
self.log_packets = log_packets
|
56
66
|
|
57
67
|
def process_packet(self, *args, **kwargs):
|
58
68
|
packet = self._client.decode_packet(*args, **kwargs)
|
@@ -73,13 +83,15 @@ class APRSDListenThread(rx.APRSDRXThread):
|
|
73
83
|
if self.packet_filter:
|
74
84
|
filter_class = filters[self.packet_filter]
|
75
85
|
if isinstance(packet, filter_class):
|
76
|
-
|
86
|
+
if self.log_packets:
|
87
|
+
packet_log.log(packet)
|
77
88
|
if self.plugin_manager:
|
78
89
|
# Don't do anything with the reply
|
79
90
|
# This is the listen only command.
|
80
91
|
self.plugin_manager.run(packet)
|
81
92
|
else:
|
82
|
-
|
93
|
+
if self.log_packets:
|
94
|
+
packet_log.log(packet)
|
83
95
|
if self.plugin_manager:
|
84
96
|
# Don't do anything with the reply.
|
85
97
|
# This is the listen only command.
|
@@ -88,6 +100,44 @@ class APRSDListenThread(rx.APRSDRXThread):
|
|
88
100
|
packet_collector.PacketCollector().rx(packet)
|
89
101
|
|
90
102
|
|
103
|
+
class ListenStatsThread(APRSDThread):
|
104
|
+
"""Log the stats from the PacketList."""
|
105
|
+
|
106
|
+
def __init__(self):
|
107
|
+
super().__init__("PacketStatsLog")
|
108
|
+
self._last_total_rx = 0
|
109
|
+
|
110
|
+
def loop(self):
|
111
|
+
if self.loop_count % 10 == 0:
|
112
|
+
# log the stats every 10 seconds
|
113
|
+
stats_json = collector.Collector().collect()
|
114
|
+
stats = stats_json["PacketList"]
|
115
|
+
total_rx = stats["rx"]
|
116
|
+
packet_count = len(stats["packets"])
|
117
|
+
rx_delta = total_rx - self._last_total_rx
|
118
|
+
rate = rx_delta / 10
|
119
|
+
|
120
|
+
# Log summary stats
|
121
|
+
LOGU.opt(colors=True).info(
|
122
|
+
f"<green>RX Rate: {rate} pps</green> "
|
123
|
+
f"<yellow>Total RX: {total_rx}</yellow> "
|
124
|
+
f"<red>RX Last 10 secs: {rx_delta}</red> "
|
125
|
+
f"<white>Packets in PacketList: {packet_count}</white>",
|
126
|
+
)
|
127
|
+
self._last_total_rx = total_rx
|
128
|
+
|
129
|
+
# Log individual type stats
|
130
|
+
for k, v in stats["types"].items():
|
131
|
+
thread_hex = f"fg {utils.hex_from_name(k)}"
|
132
|
+
LOGU.opt(colors=True).info(
|
133
|
+
f"<{thread_hex}>{k:<15}</{thread_hex}> "
|
134
|
+
f"<blue>RX: {v['rx']}</blue> <red>TX: {v['tx']}</red>",
|
135
|
+
)
|
136
|
+
|
137
|
+
time.sleep(1)
|
138
|
+
return True
|
139
|
+
|
140
|
+
|
91
141
|
@cli.command()
|
92
142
|
@cli_helper.add_options(cli_helper.common_options)
|
93
143
|
@click.option(
|
@@ -122,6 +172,11 @@ class APRSDListenThread(rx.APRSDRXThread):
|
|
122
172
|
),
|
123
173
|
help="Filter by packet type",
|
124
174
|
)
|
175
|
+
@click.option(
|
176
|
+
"--enable-plugin",
|
177
|
+
multiple=True,
|
178
|
+
help="Enable a plugin. This is the name of the file in the plugins directory.",
|
179
|
+
)
|
125
180
|
@click.option(
|
126
181
|
"--load-plugins",
|
127
182
|
default=False,
|
@@ -133,6 +188,18 @@ class APRSDListenThread(rx.APRSDRXThread):
|
|
133
188
|
nargs=-1,
|
134
189
|
required=True,
|
135
190
|
)
|
191
|
+
@click.option(
|
192
|
+
"--log-packets",
|
193
|
+
default=False,
|
194
|
+
is_flag=True,
|
195
|
+
help="Log incoming packets.",
|
196
|
+
)
|
197
|
+
@click.option(
|
198
|
+
"--enable-packet-stats",
|
199
|
+
default=False,
|
200
|
+
is_flag=True,
|
201
|
+
help="Enable packet stats periodic logging.",
|
202
|
+
)
|
136
203
|
@click.pass_context
|
137
204
|
@cli_helper.process_standard_options
|
138
205
|
def listen(
|
@@ -140,8 +207,11 @@ def listen(
|
|
140
207
|
aprs_login,
|
141
208
|
aprs_password,
|
142
209
|
packet_filter,
|
210
|
+
enable_plugin,
|
143
211
|
load_plugins,
|
144
212
|
filter,
|
213
|
+
log_packets,
|
214
|
+
enable_packet_stats,
|
145
215
|
):
|
146
216
|
"""Listen to packets on the APRS-IS Network based on FILTER.
|
147
217
|
|
@@ -190,27 +260,43 @@ def listen(
|
|
190
260
|
LOG.info("Creating client connection")
|
191
261
|
aprs_client = client_factory.create()
|
192
262
|
LOG.info(aprs_client)
|
263
|
+
if not aprs_client.login_success:
|
264
|
+
# We failed to login, will just quit!
|
265
|
+
msg = f"Login Failure: {aprs_client.login_failure}"
|
266
|
+
LOG.error(msg)
|
267
|
+
print(msg)
|
268
|
+
sys.exit(-1)
|
193
269
|
|
194
270
|
LOG.debug(f"Filter by '{filter}'")
|
195
271
|
aprs_client.set_filter(filter)
|
196
272
|
|
197
|
-
|
198
|
-
# keepalive.start()
|
273
|
+
keepalive_thread = keepalive.KeepAliveThread()
|
199
274
|
|
200
275
|
if not CONF.enable_seen_list:
|
201
276
|
# just deregister the class from the packet collector
|
202
277
|
packet_collector.PacketCollector().unregister(seen_list.SeenList)
|
203
278
|
|
204
279
|
pm = None
|
205
|
-
pm = plugin.PluginManager()
|
206
280
|
if load_plugins:
|
281
|
+
pm = plugin.PluginManager()
|
207
282
|
LOG.info("Loading plugins")
|
208
283
|
pm.setup_plugins(load_help_plugin=False)
|
284
|
+
elif enable_plugin:
|
285
|
+
pm = plugin.PluginManager()
|
286
|
+
pm.setup_plugins(
|
287
|
+
load_help_plugin=False,
|
288
|
+
plugin_list=enable_plugin,
|
289
|
+
)
|
209
290
|
else:
|
210
291
|
LOG.warning(
|
211
292
|
"Not Loading any plugins use --load-plugins to load what's "
|
212
293
|
"defined in the config file.",
|
213
294
|
)
|
295
|
+
|
296
|
+
if pm:
|
297
|
+
for p in pm.get_plugins():
|
298
|
+
LOG.info("Loaded plugin %s", p.__class__.__name__)
|
299
|
+
|
214
300
|
stats = stats_thread.APRSDStatsStoreThread()
|
215
301
|
stats.start()
|
216
302
|
|
@@ -219,13 +305,18 @@ def listen(
|
|
219
305
|
packet_queue=threads.packet_queue,
|
220
306
|
packet_filter=packet_filter,
|
221
307
|
plugin_manager=pm,
|
308
|
+
enabled_plugins=enable_plugin,
|
309
|
+
log_packets=log_packets,
|
222
310
|
)
|
223
311
|
LOG.debug("Start APRSDListenThread")
|
224
312
|
listen_thread.start()
|
313
|
+
if enable_packet_stats:
|
314
|
+
listen_stats = ListenStatsThread()
|
315
|
+
listen_stats.start()
|
225
316
|
|
226
|
-
|
317
|
+
keepalive_thread.start()
|
227
318
|
LOG.debug("keepalive Join")
|
228
|
-
|
319
|
+
keepalive_thread.join()
|
229
320
|
LOG.debug("listen_thread Join")
|
230
321
|
listen_thread.join()
|
231
322
|
stats.join()
|