aprsd 3.2.2__py2.py3-none-any.whl → 3.3.1__py2.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 CHANGED
@@ -50,6 +50,40 @@ common_options = [
50
50
  ]
51
51
 
52
52
 
53
+ class AliasedGroup(click.Group):
54
+ def command(self, *args, **kwargs):
55
+ """A shortcut decorator for declaring and attaching a command to
56
+ the group. This takes the same arguments as :func:`command` but
57
+ immediately registers the created command with this instance by
58
+ calling into :meth:`add_command`.
59
+ Copied from `click` and extended for `aliases`.
60
+ """
61
+ def decorator(f):
62
+ aliases = kwargs.pop("aliases", [])
63
+ cmd = click.decorators.command(*args, **kwargs)(f)
64
+ self.add_command(cmd)
65
+ for alias in aliases:
66
+ self.add_command(cmd, name=alias)
67
+ return cmd
68
+ return decorator
69
+
70
+ def group(self, *args, **kwargs):
71
+ """A shortcut decorator for declaring and attaching a group to
72
+ the group. This takes the same arguments as :func:`group` but
73
+ immediately registers the created command with this instance by
74
+ calling into :meth:`add_command`.
75
+ Copied from `click` and extended for `aliases`.
76
+ """
77
+ def decorator(f):
78
+ aliases = kwargs.pop("aliases", [])
79
+ cmd = click.decorators.group(*args, **kwargs)(f)
80
+ self.add_command(cmd)
81
+ for alias in aliases:
82
+ self.add_command(cmd, name=alias)
83
+ return cmd
84
+ return decorator
85
+
86
+
53
87
  def add_options(options):
54
88
  def _add_options(func):
55
89
  for option in reversed(options):
@@ -104,7 +138,7 @@ def process_standard_options_no_config(f: F) -> F:
104
138
  ctx.obj["loglevel"] = kwargs["loglevel"]
105
139
  ctx.obj["config_file"] = kwargs["config_file"]
106
140
  ctx.obj["quiet"] = kwargs["quiet"]
107
- log.setup_logging_no_config(
141
+ log.setup_logging(
108
142
  ctx.obj["loglevel"],
109
143
  ctx.obj["quiet"],
110
144
  )
aprsd/client.py CHANGED
@@ -25,7 +25,7 @@ TRANSPORT_FAKE = "fake"
25
25
  factory = None
26
26
 
27
27
 
28
- class Client(metaclass=trace.TraceWrapperMetaclass):
28
+ class Client:
29
29
  """Singleton client class that constructs the aprslib connection."""
30
30
 
31
31
  _instance = None
@@ -90,7 +90,7 @@ class Client(metaclass=trace.TraceWrapperMetaclass):
90
90
  pass
91
91
 
92
92
 
93
- class APRSISClient(Client, metaclass=trace.TraceWrapperMetaclass):
93
+ class APRSISClient(Client):
94
94
 
95
95
  _client = None
96
96
 
@@ -175,7 +175,7 @@ class APRSISClient(Client, metaclass=trace.TraceWrapperMetaclass):
175
175
  return aprs_client
176
176
 
177
177
 
178
- class KISSClient(Client, metaclass=trace.TraceWrapperMetaclass):
178
+ class KISSClient(Client):
179
179
 
180
180
  _client = None
181
181
 
aprsd/clients/aprsis.py CHANGED
@@ -112,7 +112,6 @@ class Aprsdis(aprslib.IS):
112
112
  self._sendall(login_str)
113
113
  self.sock.settimeout(5)
114
114
  test = self.sock.recv(len(login_str) + 100)
115
- self.logger.debug("Server: '%s'", test)
116
115
  if is_py3:
117
116
  test = test.decode("latin-1")
118
117
  test = test.rstrip()
aprsd/cmds/dev.py CHANGED
@@ -125,8 +125,37 @@ def test_plugin(
125
125
  LOG.info(f"P'{plugin_path}' F'{fromcall}' C'{message}'")
126
126
 
127
127
  for x in range(number):
128
- reply = pm.run(packet)
128
+ replies = pm.run(packet)
129
129
  # Plugin might have threads, so lets stop them so we can exit.
130
130
  # obj.stop_threads()
131
- LOG.info(f"Result{x} = '{reply}'")
131
+ for reply in replies:
132
+ if isinstance(reply, list):
133
+ # one of the plugins wants to send multiple messages
134
+ for subreply in reply:
135
+ if isinstance(subreply, packets.Packet):
136
+ LOG.info(subreply)
137
+ else:
138
+ LOG.info(
139
+ packets.MessagePacket(
140
+ from_call=CONF.callsign,
141
+ to_call=fromcall,
142
+ message_text=subreply,
143
+ ),
144
+ )
145
+ elif isinstance(reply, packets.Packet):
146
+ # We have a message based object.
147
+ LOG.info(reply)
148
+ else:
149
+ # A plugin can return a null message flag which signals
150
+ # us that they processed the message correctly, but have
151
+ # nothing to reply with, so we avoid replying with a
152
+ # usage string
153
+ if reply is not packets.NULL_MESSAGE:
154
+ LOG.info(
155
+ packets.MessagePacket(
156
+ from_call=CONF.callsign,
157
+ to_call=fromcall,
158
+ message_text=reply,
159
+ ),
160
+ )
132
161
  pm.stop()
aprsd/cmds/fetch_stats.py CHANGED
@@ -58,7 +58,11 @@ def fetch_stats(ctx, host, port, magic_word):
58
58
  with console.status(msg):
59
59
  client = rpc_client.RPCClient(host, port, magic_word)
60
60
  stats = client.get_stats_dict()
61
- console.print_json(data=stats)
61
+ if stats:
62
+ console.print_json(data=stats)
63
+ else:
64
+ LOG.error(f"Failed to fetch stats via RPC aprsd server at {host}:{port}")
65
+ return
62
66
  aprsd_title = (
63
67
  "APRSD "
64
68
  f"[bold cyan]v{stats['aprsd']['version']}[/] "
@@ -26,6 +26,7 @@ from aprsd.plugins import (
26
26
 
27
27
 
28
28
  LOG = logging.getLogger("APRSD")
29
+ PYPI_URL = "https://pypi.org/search/"
29
30
 
30
31
 
31
32
  def onerror(name):
@@ -89,18 +90,35 @@ def get_module_info(package_name, module_name, module_path):
89
90
  return obj_list
90
91
 
91
92
 
92
- def get_installed_plugins():
93
+ def _get_installed_aprsd_items():
93
94
  # installed plugins
94
- ip = {}
95
+ plugins = {}
96
+ extensions = {}
95
97
  for finder, name, ispkg in pkgutil.iter_modules():
96
98
  if name.startswith("aprsd_"):
99
+ print(f"Found aprsd_ module: {name}")
97
100
  if ispkg:
98
101
  module = importlib.import_module(name)
99
102
  pkgs = walk_package(module)
100
103
  for pkg in pkgs:
101
104
  pkg_info = get_module_info(module.__name__, pkg.name, module.__path__[0])
102
- ip[name] = pkg_info
103
- return ip
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
104
122
 
105
123
 
106
124
  def show_built_in_plugins(console):
@@ -144,22 +162,27 @@ def show_built_in_plugins(console):
144
162
  console.print(table)
145
163
 
146
164
 
147
- def show_pypi_plugins(installed_plugins, console):
165
+ def _get_pypi_packages():
148
166
  query = "aprsd"
149
- api_url = "https://pypi.org/search/"
150
167
  snippets = []
151
168
  s = requests.Session()
152
169
  for page in range(1, 3):
153
170
  params = {"q": query, "page": page}
154
- r = s.get(api_url, params=params)
171
+ r = s.get(PYPI_URL, params=params)
155
172
  soup = BeautifulSoup(r.text, "html.parser")
156
173
  snippets += soup.select('a[class*="snippet"]')
157
174
  if not hasattr(s, "start_url"):
158
175
  s.start_url = r.url.rsplit("&page", maxsplit=1).pop(0)
159
176
 
177
+ return snippets
178
+
179
+
180
+ def show_pypi_plugins(installed_plugins, console):
181
+ snippets = _get_pypi_packages()
182
+
160
183
  title = Text.assemble(
161
184
  ("Pypi.org APRSD Installable Plugin Packages\n\n", "bold magenta"),
162
- ("Install any of the following plugins with ", "bold yellow"),
185
+ ("Install any of the following plugins with\n", "bold yellow"),
163
186
  ("'pip install ", "bold white"),
164
187
  ("<Plugin Package Name>'", "cyan"),
165
188
  )
@@ -171,7 +194,7 @@ def show_pypi_plugins(installed_plugins, console):
171
194
  table.add_column("Released", style="bold green", justify="center")
172
195
  table.add_column("Installed?", style="red", justify="center")
173
196
  for snippet in snippets:
174
- link = urljoin(api_url, snippet.get("href"))
197
+ link = urljoin(PYPI_URL, snippet.get("href"))
175
198
  package = re.sub(r"\s+", " ", snippet.select_one('span[class*="name"]').text.strip())
176
199
  version = re.sub(r"\s+", " ", snippet.select_one('span[class*="version"]').text.strip())
177
200
  created = re.sub(r"\s+", " ", snippet.select_one('span[class*="created"]').text.strip())
@@ -194,7 +217,47 @@ def show_pypi_plugins(installed_plugins, console):
194
217
 
195
218
  console.print("\n")
196
219
  console.print(table)
197
- return
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)
198
261
 
199
262
 
200
263
  def show_installed_plugins(installed_plugins, console):
@@ -240,3 +303,17 @@ def list_plugins(ctx):
240
303
 
241
304
  status.update("Looking for installed APRSD plugins")
242
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)
aprsd/cmds/server.py CHANGED
@@ -11,7 +11,7 @@ from aprsd import main as aprsd_main
11
11
  from aprsd import packets, plugin, threads, utils
12
12
  from aprsd.main import cli
13
13
  from aprsd.rpc import server as rpc_server
14
- from aprsd.threads import rx
14
+ from aprsd.threads import registry, rx, tx
15
15
 
16
16
 
17
17
  CONF = cfg.CONF
@@ -107,6 +107,15 @@ def server(ctx, flush):
107
107
  process_thread.start()
108
108
 
109
109
  packets.PacketTrack().restart()
110
+ if CONF.enable_beacon:
111
+ LOG.info("Beacon Enabled. Starting Beacon thread.")
112
+ bcn_thread = tx.BeaconSendThread()
113
+ bcn_thread.start()
114
+
115
+ if CONF.aprs_registry.enabled:
116
+ LOG.info("Registry Enabled. Starting Registry thread.")
117
+ registry_thread = registry.APRSRegistryThread()
118
+ registry_thread.start()
110
119
 
111
120
  if CONF.rpc_settings.enabled:
112
121
  rpc = rpc_server.APRSDRPCThread()