aprsd 3.4.4__py3-none-any.whl → 4.0.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/cli_helper.py +12 -5
- aprsd/client/aprsis.py +31 -9
- aprsd/client/base.py +13 -2
- aprsd/client/drivers/aprsis.py +6 -3
- aprsd/client/drivers/fake.py +15 -20
- aprsd/client/factory.py +0 -2
- aprsd/client/fake.py +0 -2
- aprsd/client/kiss.py +17 -2
- aprsd/client/stats.py +1 -3
- aprsd/cmds/completion.py +7 -4
- aprsd/cmds/dev.py +38 -42
- aprsd/cmds/fetch_stats.py +140 -143
- aprsd/cmds/healthcheck.py +5 -3
- aprsd/cmds/list_plugins.py +140 -134
- aprsd/cmds/listen.py +13 -9
- aprsd/cmds/server.py +53 -27
- aprsd/conf/__init__.py +1 -2
- aprsd/conf/client.py +3 -4
- aprsd/conf/common.py +19 -93
- 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 +10 -4
- aprsd/packets/__init__.py +14 -4
- aprsd/packets/core.py +57 -67
- aprsd/packets/log.py +8 -8
- aprsd/packets/packet_list.py +9 -6
- aprsd/plugin.py +22 -11
- aprsd/plugins/notify.py +1 -4
- aprsd/plugins/weather.py +10 -8
- aprsd/stats/collector.py +5 -2
- aprsd/threads/__init__.py +3 -2
- aprsd/threads/aprsd.py +12 -7
- aprsd/threads/{keep_alive.py → keepalive.py} +14 -45
- aprsd/threads/registry.py +3 -3
- aprsd/threads/rx.py +9 -6
- aprsd/threads/stats.py +2 -2
- aprsd/threads/tx.py +3 -4
- aprsd/utils/__init__.py +42 -10
- aprsd/utils/json.py +9 -4
- aprsd/utils/keepalive_collector.py +55 -0
- aprsd/utils/trace.py +0 -2
- aprsd-4.0.1.dist-info/AUTHORS +1 -0
- {aprsd-3.4.4.dist-info → aprsd-4.0.1.dist-info}/METADATA +307 -408
- aprsd-4.0.1.dist-info/RECORD +74 -0
- {aprsd-3.4.4.dist-info → aprsd-4.0.1.dist-info}/WHEEL +1 -1
- aprsd/cmds/admin.py +0 -57
- aprsd/cmds/webchat.py +0 -662
- aprsd/conf/plugin_email.py +0 -105
- aprsd/plugins/email.py +0 -715
- aprsd/plugins/location.py +0 -181
- 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 -612
- 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 -322
- aprsd-3.4.4.dist-info/AUTHORS +0 -13
- aprsd-3.4.4.dist-info/RECORD +0 -134
- {aprsd-3.4.4.dist-info → aprsd-4.0.1.dist-info}/LICENSE +0 -0
- {aprsd-3.4.4.dist-info → aprsd-4.0.1.dist-info}/entry_points.txt +0 -0
- {aprsd-3.4.4.dist-info → aprsd-4.0.1.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
@@ -23,11 +23,10 @@ from aprsd.packets import collector as packet_collector
|
|
23
23
|
from aprsd.packets import log as packet_log
|
24
24
|
from aprsd.packets import seen_list
|
25
25
|
from aprsd.stats import collector
|
26
|
-
from aprsd.threads import
|
26
|
+
from aprsd.threads import keepalive, rx
|
27
27
|
from aprsd.threads import stats as stats_thread
|
28
28
|
from aprsd.threads.aprsd import APRSDThread
|
29
29
|
|
30
|
-
|
31
30
|
# setup the global logger
|
32
31
|
# log.basicConfig(level=log.DEBUG) # level=10
|
33
32
|
LOG = logging.getLogger("APRSD")
|
@@ -51,8 +50,12 @@ def signal_handler(sig, frame):
|
|
51
50
|
|
52
51
|
class APRSDListenThread(rx.APRSDRXThread):
|
53
52
|
def __init__(
|
54
|
-
self,
|
55
|
-
|
53
|
+
self,
|
54
|
+
packet_queue,
|
55
|
+
packet_filter=None,
|
56
|
+
plugin_manager=None,
|
57
|
+
enabled_plugins=[],
|
58
|
+
log_packets=False,
|
56
59
|
):
|
57
60
|
super().__init__(packet_queue)
|
58
61
|
self.packet_filter = packet_filter
|
@@ -88,7 +91,6 @@ class APRSDListenThread(rx.APRSDRXThread):
|
|
88
91
|
self.plugin_manager.run(packet)
|
89
92
|
else:
|
90
93
|
if self.log_packets:
|
91
|
-
LOG.error("PISS")
|
92
94
|
packet_log.log(packet)
|
93
95
|
if self.plugin_manager:
|
94
96
|
# Don't do anything with the reply.
|
@@ -111,6 +113,7 @@ class ListenStatsThread(APRSDThread):
|
|
111
113
|
stats_json = collector.Collector().collect()
|
112
114
|
stats = stats_json["PacketList"]
|
113
115
|
total_rx = stats["rx"]
|
116
|
+
packet_count = len(stats["packets"])
|
114
117
|
rx_delta = total_rx - self._last_total_rx
|
115
118
|
rate = rx_delta / 10
|
116
119
|
|
@@ -118,7 +121,8 @@ class ListenStatsThread(APRSDThread):
|
|
118
121
|
LOGU.opt(colors=True).info(
|
119
122
|
f"<green>RX Rate: {rate} pps</green> "
|
120
123
|
f"<yellow>Total RX: {total_rx}</yellow> "
|
121
|
-
f"<red>RX Last 10 secs: {rx_delta}</red>"
|
124
|
+
f"<red>RX Last 10 secs: {rx_delta}</red> "
|
125
|
+
f"<white>Packets in PacketList: {packet_count}</white>",
|
122
126
|
)
|
123
127
|
self._last_total_rx = total_rx
|
124
128
|
|
@@ -266,7 +270,7 @@ def listen(
|
|
266
270
|
LOG.debug(f"Filter by '{filter}'")
|
267
271
|
aprs_client.set_filter(filter)
|
268
272
|
|
269
|
-
|
273
|
+
keepalive_thread = keepalive.KeepAliveThread()
|
270
274
|
|
271
275
|
if not CONF.enable_seen_list:
|
272
276
|
# just deregister the class from the packet collector
|
@@ -310,9 +314,9 @@ def listen(
|
|
310
314
|
listen_stats = ListenStatsThread()
|
311
315
|
listen_stats.start()
|
312
316
|
|
313
|
-
|
317
|
+
keepalive_thread.start()
|
314
318
|
LOG.debug("keepalive Join")
|
315
|
-
|
319
|
+
keepalive_thread.join()
|
316
320
|
LOG.debug("listen_thread Join")
|
317
321
|
listen_thread.join()
|
318
322
|
stats.join()
|
aprsd/cmds/server.py
CHANGED
@@ -6,22 +6,54 @@ import click
|
|
6
6
|
from oslo_config import cfg
|
7
7
|
|
8
8
|
import aprsd
|
9
|
-
from aprsd import cli_helper
|
9
|
+
from aprsd import cli_helper, plugin, threads, utils
|
10
10
|
from aprsd import main as aprsd_main
|
11
|
-
from aprsd import plugin, threads, utils
|
12
11
|
from aprsd.client import client_factory
|
13
12
|
from aprsd.main import cli
|
14
13
|
from aprsd.packets import collector as packet_collector
|
15
14
|
from aprsd.packets import seen_list
|
16
|
-
from aprsd.threads import
|
15
|
+
from aprsd.threads import aprsd as aprsd_threads
|
16
|
+
from aprsd.threads import keepalive, registry, rx, tx
|
17
17
|
from aprsd.threads import stats as stats_thread
|
18
|
-
from aprsd.
|
19
|
-
|
18
|
+
from aprsd.utils import singleton
|
20
19
|
|
21
20
|
CONF = cfg.CONF
|
22
21
|
LOG = logging.getLogger("APRSD")
|
23
22
|
|
24
23
|
|
24
|
+
@singleton
|
25
|
+
class ServerThreads:
|
26
|
+
"""Registry for threads that the server command runs.
|
27
|
+
|
28
|
+
This enables extensions to register a thread to run during
|
29
|
+
the server command.
|
30
|
+
|
31
|
+
"""
|
32
|
+
|
33
|
+
def __init__(self):
|
34
|
+
self.threads: list[aprsd_threads.APRSDThread] = []
|
35
|
+
|
36
|
+
def register(self, thread: aprsd_threads.APRSDThread):
|
37
|
+
if not isinstance(thread, aprsd_threads.APRSDThread):
|
38
|
+
raise TypeError(f"Thread {thread} is not an APRSDThread")
|
39
|
+
self.threads.append(thread)
|
40
|
+
|
41
|
+
def unregister(self, thread: aprsd_threads.APRSDThread):
|
42
|
+
if not isinstance(thread, aprsd_threads.APRSDThread):
|
43
|
+
raise TypeError(f"Thread {thread} is not an APRSDThread")
|
44
|
+
self.threads.remove(thread)
|
45
|
+
|
46
|
+
def start(self):
|
47
|
+
"""Start all threads in the list."""
|
48
|
+
for thread in self.threads:
|
49
|
+
thread.start()
|
50
|
+
|
51
|
+
def join(self):
|
52
|
+
"""Join all the threads in the list"""
|
53
|
+
for thread in self.threads:
|
54
|
+
thread.join()
|
55
|
+
|
56
|
+
|
25
57
|
# main() ###
|
26
58
|
@cli.command()
|
27
59
|
@cli_helper.add_options(cli_helper.common_options)
|
@@ -41,6 +73,8 @@ def server(ctx, flush):
|
|
41
73
|
signal.signal(signal.SIGINT, aprsd_main.signal_handler)
|
42
74
|
signal.signal(signal.SIGTERM, aprsd_main.signal_handler)
|
43
75
|
|
76
|
+
server_threads = ServerThreads()
|
77
|
+
|
44
78
|
level, msg = utils._check_version()
|
45
79
|
if level:
|
46
80
|
LOG.warning(msg)
|
@@ -110,36 +144,28 @@ def server(ctx, flush):
|
|
110
144
|
|
111
145
|
# Now start all the main processing threads.
|
112
146
|
|
113
|
-
keepalive
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
rx_thread = rx.APRSDPluginRXThread(
|
120
|
-
packet_queue=threads.packet_queue,
|
147
|
+
server_threads.register(keepalive.KeepAliveThread())
|
148
|
+
server_threads.register(stats_thread.APRSDStatsStoreThread())
|
149
|
+
server_threads.register(
|
150
|
+
rx.APRSDPluginRXThread(
|
151
|
+
packet_queue=threads.packet_queue,
|
152
|
+
),
|
121
153
|
)
|
122
|
-
|
123
|
-
|
154
|
+
server_threads.register(
|
155
|
+
rx.APRSDPluginProcessPacketThread(
|
156
|
+
packet_queue=threads.packet_queue,
|
157
|
+
),
|
124
158
|
)
|
125
|
-
rx_thread.start()
|
126
|
-
process_thread.start()
|
127
159
|
|
128
160
|
if CONF.enable_beacon:
|
129
161
|
LOG.info("Beacon Enabled. Starting Beacon thread.")
|
130
|
-
|
131
|
-
bcn_thread.start()
|
162
|
+
server_threads.register(tx.BeaconSendThread())
|
132
163
|
|
133
164
|
if CONF.aprs_registry.enabled:
|
134
165
|
LOG.info("Registry Enabled. Starting Registry thread.")
|
135
|
-
|
136
|
-
registry_thread.start()
|
137
|
-
|
138
|
-
if CONF.admin.web_enabled:
|
139
|
-
log_monitor_thread = log_monitor.LogMonitorThread()
|
140
|
-
log_monitor_thread.start()
|
166
|
+
server_threads.register(registry.APRSRegistryThread())
|
141
167
|
|
142
|
-
|
143
|
-
|
168
|
+
server_threads.start()
|
169
|
+
server_threads.join()
|
144
170
|
|
145
171
|
return 0
|
aprsd/conf/__init__.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
from oslo_config import cfg
|
2
2
|
|
3
|
-
from aprsd.conf import client, common, log, plugin_common
|
3
|
+
from aprsd.conf import client, common, log, plugin_common
|
4
4
|
|
5
5
|
|
6
6
|
CONF = cfg.CONF
|
@@ -11,7 +11,6 @@ client.register_opts(CONF)
|
|
11
11
|
|
12
12
|
# plugins
|
13
13
|
plugin_common.register_opts(CONF)
|
14
|
-
plugin_email.register_opts(CONF)
|
15
14
|
|
16
15
|
|
17
16
|
def set_lib_defaults():
|
aprsd/conf/client.py
CHANGED
@@ -4,7 +4,6 @@ The options for log setup
|
|
4
4
|
|
5
5
|
from oslo_config import cfg
|
6
6
|
|
7
|
-
|
8
7
|
DEFAULT_LOGIN = "NOCALL"
|
9
8
|
|
10
9
|
aprs_group = cfg.OptGroup(
|
@@ -31,7 +30,7 @@ aprs_opts = [
|
|
31
30
|
"enabled",
|
32
31
|
default=True,
|
33
32
|
help="Set enabled to False if there is no internet connectivity."
|
34
|
-
|
33
|
+
"This is useful for a direwolf KISS aprs connection only.",
|
35
34
|
),
|
36
35
|
cfg.StrOpt(
|
37
36
|
"login",
|
@@ -42,8 +41,8 @@ aprs_opts = [
|
|
42
41
|
"password",
|
43
42
|
secret=True,
|
44
43
|
help="APRS Password "
|
45
|
-
|
46
|
-
|
44
|
+
"Get the passcode for your callsign here: "
|
45
|
+
"https://apps.magicbug.co.uk/passcode",
|
47
46
|
),
|
48
47
|
cfg.HostAddressOpt(
|
49
48
|
"host",
|