aprsd 3.4.4__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.
Files changed (115) hide show
  1. aprsd/cli_helper.py +12 -5
  2. aprsd/client/aprsis.py +31 -9
  3. aprsd/client/base.py +13 -2
  4. aprsd/client/drivers/aprsis.py +6 -3
  5. aprsd/client/drivers/fake.py +15 -20
  6. aprsd/client/factory.py +0 -2
  7. aprsd/client/fake.py +0 -2
  8. aprsd/client/kiss.py +17 -2
  9. aprsd/client/stats.py +1 -3
  10. aprsd/cmds/completion.py +7 -4
  11. aprsd/cmds/dev.py +38 -42
  12. aprsd/cmds/fetch_stats.py +140 -143
  13. aprsd/cmds/healthcheck.py +5 -3
  14. aprsd/cmds/list_plugins.py +140 -134
  15. aprsd/cmds/listen.py +13 -9
  16. aprsd/cmds/server.py +53 -27
  17. aprsd/conf/__init__.py +1 -2
  18. aprsd/conf/client.py +3 -4
  19. aprsd/conf/common.py +19 -93
  20. aprsd/conf/log.py +2 -4
  21. aprsd/conf/opts.py +5 -4
  22. aprsd/conf/plugin_common.py +11 -121
  23. aprsd/exception.py +2 -0
  24. aprsd/log/log.py +7 -46
  25. aprsd/main.py +10 -4
  26. aprsd/packets/__init__.py +14 -4
  27. aprsd/packets/core.py +57 -67
  28. aprsd/packets/log.py +8 -8
  29. aprsd/packets/packet_list.py +9 -6
  30. aprsd/plugin.py +22 -11
  31. aprsd/plugins/notify.py +1 -4
  32. aprsd/plugins/weather.py +10 -8
  33. aprsd/stats/collector.py +5 -2
  34. aprsd/threads/__init__.py +3 -2
  35. aprsd/threads/aprsd.py +12 -7
  36. aprsd/threads/{keep_alive.py → keepalive.py} +14 -45
  37. aprsd/threads/registry.py +3 -3
  38. aprsd/threads/rx.py +9 -6
  39. aprsd/threads/stats.py +2 -2
  40. aprsd/threads/tx.py +3 -4
  41. aprsd/utils/__init__.py +42 -10
  42. aprsd/utils/json.py +9 -4
  43. aprsd/utils/keepalive_collector.py +55 -0
  44. aprsd/utils/trace.py +0 -2
  45. aprsd-4.0.0.dist-info/AUTHORS +1 -0
  46. aprsd-4.0.0.dist-info/METADATA +293 -0
  47. aprsd-4.0.0.dist-info/RECORD +74 -0
  48. {aprsd-3.4.4.dist-info → aprsd-4.0.0.dist-info}/WHEEL +1 -1
  49. aprsd/cmds/admin.py +0 -57
  50. aprsd/cmds/webchat.py +0 -662
  51. aprsd/conf/plugin_email.py +0 -105
  52. aprsd/plugins/email.py +0 -715
  53. aprsd/plugins/location.py +0 -181
  54. aprsd/threads/log_monitor.py +0 -121
  55. aprsd/web/__init__.py +0 -0
  56. aprsd/web/admin/__init__.py +0 -0
  57. aprsd/web/admin/static/css/index.css +0 -84
  58. aprsd/web/admin/static/css/prism.css +0 -4
  59. aprsd/web/admin/static/css/tabs.css +0 -35
  60. aprsd/web/admin/static/images/Untitled.png +0 -0
  61. aprsd/web/admin/static/images/aprs-symbols-16-0.png +0 -0
  62. aprsd/web/admin/static/images/aprs-symbols-16-1.png +0 -0
  63. aprsd/web/admin/static/images/aprs-symbols-64-0.png +0 -0
  64. aprsd/web/admin/static/images/aprs-symbols-64-1.png +0 -0
  65. aprsd/web/admin/static/images/aprs-symbols-64-2.png +0 -0
  66. aprsd/web/admin/static/js/charts.js +0 -235
  67. aprsd/web/admin/static/js/echarts.js +0 -465
  68. aprsd/web/admin/static/js/logs.js +0 -26
  69. aprsd/web/admin/static/js/main.js +0 -231
  70. aprsd/web/admin/static/js/prism.js +0 -12
  71. aprsd/web/admin/static/js/send-message.js +0 -114
  72. aprsd/web/admin/static/js/tabs.js +0 -28
  73. aprsd/web/admin/templates/index.html +0 -196
  74. aprsd/web/chat/static/css/chat.css +0 -115
  75. aprsd/web/chat/static/css/index.css +0 -66
  76. aprsd/web/chat/static/css/style.css.map +0 -1
  77. aprsd/web/chat/static/css/tabs.css +0 -41
  78. aprsd/web/chat/static/css/upstream/bootstrap.min.css +0 -6
  79. aprsd/web/chat/static/css/upstream/font.woff2 +0 -0
  80. aprsd/web/chat/static/css/upstream/google-fonts.css +0 -23
  81. aprsd/web/chat/static/css/upstream/jquery-ui.css +0 -1311
  82. aprsd/web/chat/static/css/upstream/jquery.toast.css +0 -28
  83. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Bold.woff +0 -0
  84. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Bold.woff2 +0 -0
  85. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Regular.woff +0 -0
  86. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/LatoLatin-Regular.woff2 +0 -0
  87. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/icons.woff +0 -0
  88. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/icons.woff2 +0 -0
  89. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/outline-icons.woff +0 -0
  90. aprsd/web/chat/static/css/upstream/themes/default/assets/fonts/outline-icons.woff2 +0 -0
  91. aprsd/web/chat/static/images/Untitled.png +0 -0
  92. aprsd/web/chat/static/images/aprs-symbols-16-0.png +0 -0
  93. aprsd/web/chat/static/images/aprs-symbols-16-1.png +0 -0
  94. aprsd/web/chat/static/images/aprs-symbols-64-0.png +0 -0
  95. aprsd/web/chat/static/images/aprs-symbols-64-1.png +0 -0
  96. aprsd/web/chat/static/images/aprs-symbols-64-2.png +0 -0
  97. aprsd/web/chat/static/images/globe.svg +0 -3
  98. aprsd/web/chat/static/js/gps.js +0 -84
  99. aprsd/web/chat/static/js/main.js +0 -45
  100. aprsd/web/chat/static/js/send-message.js +0 -612
  101. aprsd/web/chat/static/js/tabs.js +0 -28
  102. aprsd/web/chat/static/js/upstream/bootstrap.bundle.min.js +0 -7
  103. aprsd/web/chat/static/js/upstream/jquery-3.7.1.min.js +0 -2
  104. aprsd/web/chat/static/js/upstream/jquery-ui.min.js +0 -13
  105. aprsd/web/chat/static/js/upstream/jquery.toast.js +0 -374
  106. aprsd/web/chat/static/js/upstream/semantic.min.js +0 -11
  107. aprsd/web/chat/static/js/upstream/socket.io.min.js +0 -7
  108. aprsd/web/chat/templates/index.html +0 -139
  109. aprsd/wsgi.py +0 -322
  110. aprsd-3.4.4.dist-info/AUTHORS +0 -13
  111. aprsd-3.4.4.dist-info/METADATA +0 -849
  112. aprsd-3.4.4.dist-info/RECORD +0 -134
  113. {aprsd-3.4.4.dist-info → aprsd-4.0.0.dist-info}/LICENSE +0 -0
  114. {aprsd-3.4.4.dist-info → aprsd-4.0.0.dist-info}/entry_points.txt +0 -0
  115. {aprsd-3.4.4.dist-info → aprsd-4.0.0.dist-info}/top_level.txt +0 -0
aprsd/cmds/webchat.py DELETED
@@ -1,662 +0,0 @@
1
- import datetime
2
- import json
3
- import logging
4
- import signal
5
- import sys
6
- import threading
7
- import time
8
-
9
- import click
10
- import flask
11
- from flask import request
12
- from flask_httpauth import HTTPBasicAuth
13
- from flask_socketio import Namespace, SocketIO
14
- from geopy.distance import geodesic
15
- from oslo_config import cfg
16
- import timeago
17
- from werkzeug.security import check_password_hash, generate_password_hash
18
- import wrapt
19
-
20
- import aprsd
21
- from aprsd import cli_helper, client, packets, plugin_utils, stats, threads
22
- from aprsd import utils
23
- from aprsd import utils as aprsd_utils
24
- from aprsd.client import client_factory, kiss
25
- from aprsd.main import cli
26
- from aprsd.threads import aprsd as aprsd_threads
27
- from aprsd.threads import keep_alive, rx
28
- from aprsd.threads import stats as stats_thread
29
- from aprsd.threads import tx
30
- from aprsd.utils import trace
31
-
32
-
33
- CONF = cfg.CONF
34
- LOG = logging.getLogger()
35
- auth = HTTPBasicAuth()
36
- users = {}
37
- socketio = None
38
-
39
- # List of callsigns that we don't want to track/fetch their location
40
- callsign_no_track = [
41
- "APDW16", "BLN0", "BLN1", "BLN2",
42
- "BLN3", "BLN4", "BLN5", "BLN6", "BLN7", "BLN8", "BLN9",
43
- ]
44
-
45
- # Callsign location information
46
- # callsign: {lat: 0.0, long: 0.0, last_update: datetime}
47
- callsign_locations = {}
48
-
49
- flask_app = flask.Flask(
50
- "aprsd",
51
- static_url_path="/static",
52
- static_folder="web/chat/static",
53
- template_folder="web/chat/templates",
54
- )
55
-
56
-
57
- def signal_handler(sig, frame):
58
-
59
- click.echo("signal_handler: called")
60
- LOG.info(
61
- f"Ctrl+C, Sending all threads({len(threads.APRSDThreadList())}) exit! "
62
- f"Can take up to 10 seconds {datetime.datetime.now()}",
63
- )
64
- threads.APRSDThreadList().stop_all()
65
- if "subprocess" not in str(frame):
66
- time.sleep(1.5)
67
- stats.stats_collector.collect()
68
- LOG.info("Telling flask to bail.")
69
- signal.signal(signal.SIGTERM, sys.exit(0))
70
-
71
-
72
- class SentMessages:
73
-
74
- _instance = None
75
- lock = threading.Lock()
76
-
77
- data = {}
78
-
79
- def __new__(cls, *args, **kwargs):
80
- """This magic turns this into a singleton."""
81
- if cls._instance is None:
82
- cls._instance = super().__new__(cls)
83
- return cls._instance
84
-
85
- def is_initialized(self):
86
- return True
87
-
88
- @wrapt.synchronized(lock)
89
- def add(self, msg):
90
- self.data[msg.msgNo] = msg.__dict__
91
-
92
- @wrapt.synchronized(lock)
93
- def __len__(self):
94
- return len(self.data.keys())
95
-
96
- @wrapt.synchronized(lock)
97
- def get(self, id):
98
- if id in self.data:
99
- return self.data[id]
100
-
101
- @wrapt.synchronized(lock)
102
- def get_all(self):
103
- return self.data
104
-
105
- @wrapt.synchronized(lock)
106
- def set_status(self, id, status):
107
- if id in self.data:
108
- self.data[id]["last_update"] = str(datetime.datetime.now())
109
- self.data[id]["status"] = status
110
-
111
- @wrapt.synchronized(lock)
112
- def ack(self, id):
113
- """The message got an ack!"""
114
- if id in self.data:
115
- self.data[id]["last_update"] = str(datetime.datetime.now())
116
- self.data[id]["ack"] = True
117
-
118
- @wrapt.synchronized(lock)
119
- def reply(self, id, packet):
120
- """We got a packet back from the sent message."""
121
- if id in self.data:
122
- self.data[id]["reply"] = packet
123
-
124
-
125
- # HTTPBasicAuth doesn't work on a class method.
126
- # This has to be out here. Rely on the APRSDFlask
127
- # class to initialize the users from the config
128
- @auth.verify_password
129
- def verify_password(username, password):
130
- global users
131
-
132
- if username in users and check_password_hash(users[username], password):
133
- return username
134
-
135
-
136
- def _build_location_from_repeat(message):
137
- # This is a location message Format is
138
- # ^ld^callsign:latitude,longitude,altitude,course,speed,timestamp
139
- a = message.split(":")
140
- LOG.warning(a)
141
- if len(a) == 2:
142
- callsign = a[0].replace("^ld^", "")
143
- b = a[1].split(",")
144
- LOG.warning(b)
145
- if len(b) == 6:
146
- lat = float(b[0])
147
- lon = float(b[1])
148
- alt = float(b[2])
149
- course = float(b[3])
150
- speed = float(b[4])
151
- time = int(b[5])
152
- compass_bearing = aprsd_utils.degrees_to_cardinal(course)
153
- data = {
154
- "callsign": callsign,
155
- "lat": lat,
156
- "lon": lon,
157
- "altitude": alt,
158
- "course": course,
159
- "compass_bearing": compass_bearing,
160
- "speed": speed,
161
- "lasttime": time,
162
- "timeago": timeago.format(time),
163
- }
164
- LOG.debug(f"Location data from REPEAT {data}")
165
- return data
166
-
167
-
168
- def _calculate_location_data(location_data):
169
- """Calculate all of the location data from data from aprs.fi or REPEAT."""
170
- lat = location_data["lat"]
171
- lon = location_data["lon"]
172
- alt = location_data["altitude"]
173
- speed = location_data["speed"]
174
- lasttime = location_data["lasttime"]
175
- timeago_str = location_data.get(
176
- "timeago",
177
- timeago.format(lasttime),
178
- )
179
- # now calculate distance from our own location
180
- distance = 0
181
- if CONF.webchat.latitude and CONF.webchat.longitude:
182
- our_lat = float(CONF.webchat.latitude)
183
- our_lon = float(CONF.webchat.longitude)
184
- distance = geodesic((our_lat, our_lon), (lat, lon)).kilometers
185
- bearing = aprsd_utils.calculate_initial_compass_bearing(
186
- (our_lat, our_lon),
187
- (lat, lon),
188
- )
189
- compass_bearing = aprsd_utils.degrees_to_cardinal(bearing)
190
- return {
191
- "callsign": location_data["callsign"],
192
- "lat": lat,
193
- "lon": lon,
194
- "altitude": alt,
195
- "course": f"{bearing:0.1f}",
196
- "compass_bearing": compass_bearing,
197
- "speed": speed,
198
- "lasttime": lasttime,
199
- "timeago": timeago_str,
200
- "distance": f"{distance:0.1f}",
201
- }
202
-
203
-
204
- def send_location_data_to_browser(location_data):
205
- global socketio
206
- callsign = location_data["callsign"]
207
- LOG.info(f"Got location for {callsign} {callsign_locations[callsign]}")
208
- socketio.emit(
209
- "callsign_location", callsign_locations[callsign],
210
- namespace="/sendmsg",
211
- )
212
-
213
-
214
- def populate_callsign_location(callsign, data=None):
215
- """Populate the location for the callsign.
216
-
217
- if data is passed in, then we have the location already from
218
- an APRS packet. If data is None, then we need to fetch the
219
- location from aprs.fi or REPEAT.
220
- """
221
- global socketio
222
- """Fetch the location for the callsign."""
223
- LOG.debug(f"populate_callsign_location {callsign}")
224
- if data:
225
- location_data = _calculate_location_data(data)
226
- callsign_locations[callsign] = location_data
227
- send_location_data_to_browser(location_data)
228
- return
229
-
230
- # First we are going to try to get the location from aprs.fi
231
- # if there is no internets, then this will fail and we will
232
- # fallback to calling REPEAT for the location for the callsign.
233
- fallback = False
234
- if not CONF.aprs_fi.apiKey:
235
- LOG.warning(
236
- "Config aprs_fi.apiKey is not set. Can't get location from aprs.fi "
237
- " falling back to sending REPEAT to get location.",
238
- )
239
- fallback = True
240
- else:
241
- try:
242
- aprs_data = plugin_utils.get_aprs_fi(CONF.aprs_fi.apiKey, callsign)
243
- if not len(aprs_data["entries"]):
244
- LOG.error("Didn't get any entries from aprs.fi")
245
- return
246
- lat = float(aprs_data["entries"][0]["lat"])
247
- lon = float(aprs_data["entries"][0]["lng"])
248
- try: # altitude not always provided
249
- alt = float(aprs_data["entries"][0]["altitude"])
250
- except Exception:
251
- alt = 0
252
- location_data = {
253
- "callsign": callsign,
254
- "lat": lat,
255
- "lon": lon,
256
- "altitude": alt,
257
- "lasttime": int(aprs_data["entries"][0]["lasttime"]),
258
- "course": float(aprs_data["entries"][0].get("course", 0)),
259
- "speed": float(aprs_data["entries"][0].get("speed", 0)),
260
- }
261
- location_data = _calculate_location_data(location_data)
262
- callsign_locations[callsign] = location_data
263
- send_location_data_to_browser(location_data)
264
- return
265
- except Exception as ex:
266
- LOG.error(f"Failed to fetch aprs.fi '{ex}'")
267
- LOG.error(ex)
268
- fallback = True
269
-
270
- if fallback:
271
- # We don't have the location data
272
- # and we can't get it from aprs.fi
273
- # Send a special message to REPEAT to get the location data
274
- LOG.info(f"Sending REPEAT to get location for callsign {callsign}.")
275
- tx.send(
276
- packets.MessagePacket(
277
- from_call=CONF.callsign,
278
- to_call="REPEAT",
279
- message_text=f"ld {callsign}",
280
- ),
281
- )
282
-
283
-
284
- class WebChatProcessPacketThread(rx.APRSDProcessPacketThread):
285
- """Class that handles packets being sent to us."""
286
-
287
- def __init__(self, packet_queue, socketio):
288
- self.socketio = socketio
289
- self.connected = False
290
- super().__init__(packet_queue)
291
-
292
- def process_ack_packet(self, packet: packets.AckPacket):
293
- super().process_ack_packet(packet)
294
- ack_num = packet.get("msgNo")
295
- SentMessages().ack(ack_num)
296
- msg = SentMessages().get(ack_num)
297
- if msg:
298
- self.socketio.emit(
299
- "ack", msg,
300
- namespace="/sendmsg",
301
- )
302
- self.got_ack = True
303
-
304
- def process_our_message_packet(self, packet: packets.MessagePacket):
305
- global callsign_locations
306
- # ok lets see if we have the location for the
307
- # person we just sent a message to.
308
- from_call = packet.get("from_call").upper()
309
- if from_call == "REPEAT":
310
- # We got a message from REPEAT. Is this a location message?
311
- message = packet.get("message_text")
312
- if message.startswith("^ld^"):
313
- location_data = _build_location_from_repeat(message)
314
- callsign = location_data["callsign"]
315
- location_data = _calculate_location_data(location_data)
316
- callsign_locations[callsign] = location_data
317
- send_location_data_to_browser(location_data)
318
- return
319
- elif (
320
- from_call not in callsign_locations
321
- and from_call not in callsign_no_track
322
- and client_factory.create().transport() in [client.TRANSPORT_APRSIS, client.TRANSPORT_FAKE]
323
- ):
324
- # We have to ask aprs for the location for the callsign
325
- # We send a message packet to wb4bor-11 asking for location.
326
- populate_callsign_location(from_call)
327
- # Send the packet to the browser.
328
- self.socketio.emit(
329
- "new", packet.__dict__,
330
- namespace="/sendmsg",
331
- )
332
-
333
-
334
- class LocationProcessingThread(aprsd_threads.APRSDThread):
335
- """Class to handle the location processing."""
336
- def __init__(self):
337
- super().__init__("LocationProcessingThread")
338
-
339
- def loop(self):
340
- pass
341
-
342
-
343
- def set_config():
344
- global users
345
-
346
-
347
- def _get_transport(stats):
348
- if CONF.aprs_network.enabled:
349
- transport = "aprs-is"
350
- aprs_connection = (
351
- "APRS-IS Server: <a href='http://status.aprs2.net' >"
352
- "{}</a>".format(stats["APRSClientStats"]["server_string"])
353
- )
354
- elif kiss.KISSClient.is_enabled():
355
- transport = kiss.KISSClient.transport()
356
- if transport == client.TRANSPORT_TCPKISS:
357
- aprs_connection = (
358
- "TCPKISS://{}:{}".format(
359
- CONF.kiss_tcp.host,
360
- CONF.kiss_tcp.port,
361
- )
362
- )
363
- elif transport == client.TRANSPORT_SERIALKISS:
364
- # for pep8 violation
365
- aprs_connection = (
366
- "SerialKISS://{}@{} baud".format(
367
- CONF.kiss_serial.device,
368
- CONF.kiss_serial.baudrate,
369
- ),
370
- )
371
- elif CONF.fake_client.enabled:
372
- transport = client.TRANSPORT_FAKE
373
- aprs_connection = "Fake Client"
374
-
375
- return transport, aprs_connection
376
-
377
-
378
- @flask_app.route("/location/<callsign>", methods=["POST"])
379
- def location(callsign):
380
- LOG.debug(f"Fetch location for callsign {callsign}")
381
- if not callsign in callsign_no_track:
382
- populate_callsign_location(callsign)
383
-
384
-
385
- @auth.login_required
386
- @flask_app.route("/")
387
- def index():
388
- stats = _stats()
389
-
390
- # For development
391
- html_template = "index.html"
392
- LOG.debug(f"Template {html_template}")
393
-
394
- transport, aprs_connection = _get_transport(stats["stats"])
395
- LOG.debug(f"transport {transport} aprs_connection {aprs_connection}")
396
-
397
- stats["transport"] = transport
398
- stats["aprs_connection"] = aprs_connection
399
- LOG.debug(f"initial stats = {stats}")
400
- latitude = CONF.webchat.latitude
401
- if latitude:
402
- latitude = float(CONF.webchat.latitude)
403
-
404
- longitude = CONF.webchat.longitude
405
- if longitude:
406
- longitude = float(longitude)
407
-
408
- return flask.render_template(
409
- html_template,
410
- initial_stats=stats,
411
- aprs_connection=aprs_connection,
412
- callsign=CONF.callsign,
413
- version=aprsd.__version__,
414
- latitude=latitude,
415
- longitude=longitude,
416
- )
417
-
418
-
419
- @auth.login_required
420
- @flask_app.route("/send-message-status")
421
- def send_message_status():
422
- LOG.debug(request)
423
- msgs = SentMessages()
424
- info = msgs.get_all()
425
- return json.dumps(info)
426
-
427
-
428
- def _stats():
429
- now = datetime.datetime.now()
430
-
431
- time_format = "%m-%d-%Y %H:%M:%S"
432
- stats_dict = stats.stats_collector.collect(serializable=True)
433
- # Webchat doesnt need these
434
- if "WatchList" in stats_dict:
435
- del stats_dict["WatchList"]
436
- if "SeenList" in stats_dict:
437
- del stats_dict["SeenList"]
438
- if "APRSDThreadList" in stats_dict:
439
- del stats_dict["APRSDThreadList"]
440
- if "PacketList" in stats_dict:
441
- del stats_dict["PacketList"]
442
- if "EmailStats" in stats_dict:
443
- del stats_dict["EmailStats"]
444
- if "PluginManager" in stats_dict:
445
- del stats_dict["PluginManager"]
446
-
447
- result = {
448
- "time": now.strftime(time_format),
449
- "stats": stats_dict,
450
- }
451
- return result
452
-
453
-
454
- @flask_app.route("/stats")
455
- def get_stats():
456
- return json.dumps(_stats())
457
-
458
-
459
- class SendMessageNamespace(Namespace):
460
- """Class to handle the socketio interactions."""
461
- got_ack = False
462
- reply_sent = False
463
- msg = None
464
- request = None
465
-
466
- def __init__(self, namespace=None, config=None):
467
- super().__init__(namespace)
468
-
469
- def on_connect(self):
470
- global socketio
471
- LOG.debug("Web socket connected")
472
- socketio.emit(
473
- "connected", {"data": "/sendmsg Connected"},
474
- namespace="/sendmsg",
475
- )
476
-
477
- def on_disconnect(self):
478
- LOG.debug("WS Disconnected")
479
-
480
- def on_send(self, data):
481
- global socketio
482
- LOG.debug(f"WS: on_send {data}")
483
- self.request = data
484
- data["from"] = CONF.callsign
485
- path = data.get("path", None)
486
- if not path:
487
- path = []
488
- elif "," in path:
489
- path_opts = path.split(",")
490
- path = [x.strip() for x in path_opts]
491
- else:
492
- path = [path]
493
-
494
- pkt = packets.MessagePacket(
495
- from_call=data["from"],
496
- to_call=data["to"].upper(),
497
- message_text=data["message"],
498
- path=path,
499
- )
500
- pkt.prepare()
501
- self.msg = pkt
502
- msgs = SentMessages()
503
- tx.send(pkt)
504
- msgs.add(pkt)
505
- msgs.set_status(pkt.msgNo, "Sending")
506
- obj = msgs.get(pkt.msgNo)
507
- socketio.emit(
508
- "sent", obj,
509
- namespace="/sendmsg",
510
- )
511
-
512
- def on_gps(self, data):
513
- LOG.debug(f"WS on_GPS: {data}")
514
- lat = data["latitude"]
515
- long = data["longitude"]
516
- LOG.debug(f"Lat {lat}")
517
- LOG.debug(f"Long {long}")
518
- path = data.get("path", None)
519
- if not path:
520
- path = []
521
- elif "," in path:
522
- path_opts = path.split(",")
523
- path = [x.strip() for x in path_opts]
524
- else:
525
- path = [path]
526
-
527
- tx.send(
528
- packets.BeaconPacket(
529
- from_call=CONF.callsign,
530
- to_call="APDW16",
531
- latitude=lat,
532
- longitude=long,
533
- comment="APRSD WebChat Beacon",
534
- path=path,
535
- ),
536
- direct=True,
537
- )
538
-
539
- def handle_message(self, data):
540
- LOG.debug(f"WS Data {data}")
541
-
542
- def handle_json(self, data):
543
- LOG.debug(f"WS json {data}")
544
-
545
- def on_get_callsign_location(self, data):
546
- LOG.debug(f"on_callsign_location {data}")
547
- if data["callsign"] not in callsign_no_track:
548
- populate_callsign_location(data["callsign"])
549
-
550
-
551
- @trace.trace
552
- def init_flask(loglevel, quiet):
553
- global socketio, flask_app
554
-
555
- socketio = SocketIO(
556
- flask_app, logger=False, engineio_logger=False,
557
- async_mode="threading",
558
- )
559
-
560
- socketio.on_namespace(
561
- SendMessageNamespace(
562
- "/sendmsg",
563
- ),
564
- )
565
- return socketio
566
-
567
-
568
- # main() ###
569
- @cli.command()
570
- @cli_helper.add_options(cli_helper.common_options)
571
- @click.option(
572
- "-f",
573
- "--flush",
574
- "flush",
575
- is_flag=True,
576
- show_default=True,
577
- default=False,
578
- help="Flush out all old aged messages on disk.",
579
- )
580
- @click.option(
581
- "-p",
582
- "--port",
583
- "port",
584
- show_default=True,
585
- default=None,
586
- help="Port to listen to web requests. This overrides the config.webchat.web_port setting.",
587
- )
588
- @click.pass_context
589
- @cli_helper.process_standard_options
590
- def webchat(ctx, flush, port):
591
- """Web based HAM Radio chat program!"""
592
- loglevel = ctx.obj["loglevel"]
593
- quiet = ctx.obj["quiet"]
594
-
595
- signal.signal(signal.SIGINT, signal_handler)
596
- signal.signal(signal.SIGTERM, signal_handler)
597
-
598
- level, msg = utils._check_version()
599
- if level:
600
- LOG.warning(msg)
601
- else:
602
- LOG.info(msg)
603
- LOG.info(f"APRSD Started version: {aprsd.__version__}")
604
-
605
- CONF.log_opt_values(logging.getLogger(), logging.DEBUG)
606
- user = CONF.admin.user
607
- users[user] = generate_password_hash(CONF.admin.password)
608
- if not port:
609
- port = CONF.webchat.web_port
610
-
611
- # Initialize the client factory and create
612
- # The correct client object ready for use
613
- # Make sure we have 1 client transport enabled
614
- if not client_factory.is_client_enabled():
615
- LOG.error("No Clients are enabled in config.")
616
- sys.exit(-1)
617
-
618
- if not client_factory.is_client_configured():
619
- LOG.error("APRS client is not properly configured in config file.")
620
- sys.exit(-1)
621
-
622
- # Creates the client object
623
- LOG.info("Creating client connection")
624
- aprs_client = client_factory.create()
625
- LOG.info(aprs_client)
626
- if not aprs_client.login_success:
627
- # We failed to login, will just quit!
628
- msg = f"Login Failure: {aprs_client.login_failure}"
629
- LOG.error(msg)
630
- print(msg)
631
- sys.exit(-1)
632
-
633
- keepalive = keep_alive.KeepAliveThread()
634
- LOG.info("Start KeepAliveThread")
635
- keepalive.start()
636
-
637
- stats_store_thread = stats_thread.APRSDStatsStoreThread()
638
- stats_store_thread.start()
639
-
640
- socketio = init_flask(loglevel, quiet)
641
- rx_thread = rx.APRSDPluginRXThread(
642
- packet_queue=threads.packet_queue,
643
- )
644
- rx_thread.start()
645
- process_thread = WebChatProcessPacketThread(
646
- packet_queue=threads.packet_queue,
647
- socketio=socketio,
648
- )
649
- process_thread.start()
650
-
651
- LOG.info("Start socketio.run()")
652
- socketio.run(
653
- flask_app,
654
- # This is broken for now after removing cryptography
655
- # and pyopenssl
656
- # ssl_context="adhoc",
657
- host=CONF.webchat.web_ip,
658
- port=port,
659
- allow_unsafe_werkzeug=True,
660
- )
661
-
662
- LOG.info("WebChat exiting!!!! Bye.")