mirror of
https://github.com/craigerl/aprsd.git
synced 2026-08-18 01:23:56 -04:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b2f95b0f4e | |||
| ae9e4d31ad | |||
| 65a5a90458 | |||
| 182887c20a | |||
| f228144f4b | |||
| db9e1d23d1 | |||
| 986df391b2 | |||
| 3994235380 | |||
| 9ebf2f9a30 | |||
| 011cfc55e1 | |||
| e0c3c5cbbf | |||
| 26f354b3a9 | |||
| 922a6dbb35 | |||
| d03c4fc096 |
@@ -1,9 +1,29 @@
|
|||||||
CHANGES
|
CHANGES
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
v3.1.2
|
||||||
|
------
|
||||||
|
|
||||||
|
* Added support for ThirdParty packet types
|
||||||
|
* Disable the Send GPS Beacon button
|
||||||
|
* Removed adhoc ssl support in webchat
|
||||||
|
|
||||||
|
v3.1.1
|
||||||
|
------
|
||||||
|
|
||||||
|
* Updated Changelog for v3.1.1
|
||||||
|
* Fixed pep8 failures
|
||||||
|
* re-enable USWeatherPlugin to use mapClick
|
||||||
|
* Fix sending packets over KISS interface
|
||||||
|
* Use config web\_ip for running admin ui from module
|
||||||
|
* remove loop log
|
||||||
|
* Max out the client reconnect backoff to 5
|
||||||
|
* Update the Dockerfile
|
||||||
|
|
||||||
v3.1.0
|
v3.1.0
|
||||||
------
|
------
|
||||||
|
|
||||||
|
* Changelog updates for v3.1.0
|
||||||
* Use CONF.admin.web\_port for single launch web admin
|
* Use CONF.admin.web\_port for single launch web admin
|
||||||
* Fixed sio namespace registration
|
* Fixed sio namespace registration
|
||||||
* Update Dockerfile-dev to include uwsgi
|
* Update Dockerfile-dev to include uwsgi
|
||||||
|
|||||||
+12
-2
@@ -49,8 +49,10 @@ class Client:
|
|||||||
@property
|
@property
|
||||||
def client(self):
|
def client(self):
|
||||||
if not self._client:
|
if not self._client:
|
||||||
|
LOG.info("Creating APRS client")
|
||||||
self._client = self.setup_connection()
|
self._client = self.setup_connection()
|
||||||
if self.filter:
|
if self.filter:
|
||||||
|
LOG.info("Creating APRS client filter")
|
||||||
self._client.set_filter(self.filter)
|
self._client.set_filter(self.filter)
|
||||||
return self._client
|
return self._client
|
||||||
|
|
||||||
@@ -159,7 +161,11 @@ class APRSISClient(Client):
|
|||||||
LOG.error(f"Unable to connect to APRS-IS server. '{e}' ")
|
LOG.error(f"Unable to connect to APRS-IS server. '{e}' ")
|
||||||
connected = False
|
connected = False
|
||||||
time.sleep(backoff)
|
time.sleep(backoff)
|
||||||
backoff = backoff * 2
|
# Don't allow the backoff to go to inifinity.
|
||||||
|
if backoff > 5:
|
||||||
|
backoff = 5
|
||||||
|
else:
|
||||||
|
backoff += 1
|
||||||
continue
|
continue
|
||||||
LOG.debug(f"Logging in to APRS-IS with user '{user}'")
|
LOG.debug(f"Logging in to APRS-IS with user '{user}'")
|
||||||
self._client = aprs_client
|
self._client = aprs_client
|
||||||
@@ -229,7 +235,11 @@ class KISSClient(Client):
|
|||||||
# LOG.debug(f"Decoding {msg}")
|
# LOG.debug(f"Decoding {msg}")
|
||||||
|
|
||||||
raw = aprslib.parse(str(frame))
|
raw = aprslib.parse(str(frame))
|
||||||
return core.Packet.factory(raw)
|
packet = core.Packet.factory(raw)
|
||||||
|
if isinstance(packet, core.ThirdParty):
|
||||||
|
return packet.subpacket
|
||||||
|
else:
|
||||||
|
return packet
|
||||||
|
|
||||||
@trace.trace
|
@trace.trace
|
||||||
def setup_connection(self):
|
def setup_connection(self):
|
||||||
|
|||||||
+9
-15
@@ -81,27 +81,18 @@ class KISS3Client:
|
|||||||
LOG.debug("Start blocking KISS consumer")
|
LOG.debug("Start blocking KISS consumer")
|
||||||
self._parse_callback = callback
|
self._parse_callback = callback
|
||||||
self.kiss.read(callback=self.parse_frame, min_frames=None)
|
self.kiss.read(callback=self.parse_frame, min_frames=None)
|
||||||
LOG.debug("END blocking KISS consumer")
|
LOG.debug(f"END blocking KISS consumer {self.kiss}")
|
||||||
|
|
||||||
def send(self, packet):
|
def send(self, packet):
|
||||||
"""Send an APRS Message object."""
|
"""Send an APRS Message object."""
|
||||||
|
|
||||||
# payload = (':%-9s:%s' % (
|
|
||||||
# msg.tocall,
|
|
||||||
# payload
|
|
||||||
# )).encode('US-ASCII'),
|
|
||||||
# payload = str(msg).encode('US-ASCII')
|
|
||||||
payload = None
|
payload = None
|
||||||
path = ["WIDE1-1", "WIDE2-1"]
|
path = ["WIDE1-1", "WIDE2-1"]
|
||||||
if isinstance(packet, core.AckPacket):
|
if isinstance(packet, core.Packet):
|
||||||
msg_payload = f"ack{packet.msgNo}"
|
packet.prepare()
|
||||||
elif isinstance(packet, core.Packet):
|
payload = packet.payload.encode("US-ASCII")
|
||||||
payload = packet.raw.encode("US-ASCII")
|
|
||||||
path = ["WIDE2-1"]
|
|
||||||
else:
|
else:
|
||||||
msg_payload = f"{packet.raw}{{{str(packet.msgNo)}"
|
msg_payload = f"{packet.raw}{{{str(packet.msgNo)}"
|
||||||
|
|
||||||
if not payload:
|
|
||||||
payload = (
|
payload = (
|
||||||
":{:<9}:{}".format(
|
":{:<9}:{}".format(
|
||||||
packet.to_call,
|
packet.to_call,
|
||||||
@@ -109,9 +100,12 @@ class KISS3Client:
|
|||||||
)
|
)
|
||||||
).encode("US-ASCII")
|
).encode("US-ASCII")
|
||||||
|
|
||||||
LOG.debug(f"Send '{payload}' TO KISS")
|
LOG.debug(
|
||||||
|
f"KISS Send '{payload}' TO '{packet.to_call}' From "
|
||||||
|
f"'{packet.from_call}' with PATH '{path}'",
|
||||||
|
)
|
||||||
frame = Frame.ui(
|
frame = Frame.ui(
|
||||||
destination=packet.to_call,
|
destination="APZ100",
|
||||||
source=packet.from_call,
|
source=packet.from_call,
|
||||||
path=path,
|
path=path,
|
||||||
info=payload,
|
info=payload,
|
||||||
|
|||||||
@@ -265,8 +265,12 @@ def _stats():
|
|||||||
time_format = "%m-%d-%Y %H:%M:%S"
|
time_format = "%m-%d-%Y %H:%M:%S"
|
||||||
stats_dict = stats_obj.stats()
|
stats_dict = stats_obj.stats()
|
||||||
# Webchat doesnt need these
|
# Webchat doesnt need these
|
||||||
del stats_dict["aprsd"]["watch_list"]
|
if "watch_list" in stats_dict["aprsd"]:
|
||||||
del stats_dict["aprsd"]["seen_list"]
|
del stats_dict["aprsd"]["watch_list"]
|
||||||
|
if "seen_list" in stats_dict["aprsd"]:
|
||||||
|
del stats_dict["aprsd"]["seen_list"]
|
||||||
|
if "threads" in stats_dict["aprsd"]:
|
||||||
|
del stats_dict["aprsd"]["threads"]
|
||||||
# del stats_dict["email"]
|
# del stats_dict["email"]
|
||||||
# del stats_dict["plugins"]
|
# del stats_dict["plugins"]
|
||||||
# del stats_dict["messages"]
|
# del stats_dict["messages"]
|
||||||
@@ -481,7 +485,9 @@ def webchat(ctx, flush, port):
|
|||||||
LOG.info("Start socketio.run()")
|
LOG.info("Start socketio.run()")
|
||||||
socketio.run(
|
socketio.run(
|
||||||
flask_app,
|
flask_app,
|
||||||
ssl_context="adhoc",
|
# This is broken for now after removing cryptography
|
||||||
|
# and pyopenssl
|
||||||
|
# ssl_context="adhoc",
|
||||||
host=CONF.admin.web_ip,
|
host=CONF.admin.web_ip,
|
||||||
port=port,
|
port=port,
|
||||||
)
|
)
|
||||||
|
|||||||
+95
-39
@@ -25,6 +25,7 @@ PACKET_TYPE_OBJECT = "object"
|
|||||||
PACKET_TYPE_UNKNOWN = "unknown"
|
PACKET_TYPE_UNKNOWN = "unknown"
|
||||||
PACKET_TYPE_STATUS = "status"
|
PACKET_TYPE_STATUS = "status"
|
||||||
PACKET_TYPE_BEACON = "beacon"
|
PACKET_TYPE_BEACON = "beacon"
|
||||||
|
PACKET_TYPE_THIRDPARTY = "thirdparty"
|
||||||
PACKET_TYPE_UNCOMPRESSED = "uncompressed"
|
PACKET_TYPE_UNCOMPRESSED = "uncompressed"
|
||||||
|
|
||||||
|
|
||||||
@@ -57,6 +58,8 @@ class Packet(metaclass=abc.ABCMeta):
|
|||||||
# or holds the raw string from input packet
|
# or holds the raw string from input packet
|
||||||
raw: str = None
|
raw: str = None
|
||||||
raw_dict: dict = field(repr=False, default_factory=lambda: {})
|
raw_dict: dict = field(repr=False, default_factory=lambda: {})
|
||||||
|
# Built by calling prepare(). raw needs this built first.
|
||||||
|
payload: str = None
|
||||||
|
|
||||||
# Fields related to sending packets out
|
# Fields related to sending packets out
|
||||||
send_count: int = field(repr=False, default=0)
|
send_count: int = field(repr=False, default=0)
|
||||||
@@ -92,14 +95,28 @@ class Packet(metaclass=abc.ABCMeta):
|
|||||||
def prepare(self):
|
def prepare(self):
|
||||||
"""Do stuff here that is needed prior to sending over the air."""
|
"""Do stuff here that is needed prior to sending over the air."""
|
||||||
# now build the raw message for sending
|
# now build the raw message for sending
|
||||||
|
self._build_payload()
|
||||||
self._build_raw()
|
self._build_raw()
|
||||||
|
|
||||||
|
def _build_payload(self):
|
||||||
|
"""The payload is the non headers portion of the packet."""
|
||||||
|
msg = self._filter_for_send().rstrip("\n")
|
||||||
|
self.payload = (
|
||||||
|
f":{self.to_call.ljust(9)}"
|
||||||
|
f":{msg}"
|
||||||
|
)
|
||||||
|
|
||||||
def _build_raw(self):
|
def _build_raw(self):
|
||||||
"""Build the self.raw string which is what is sent over the air."""
|
"""Build the self.raw which is what is sent over the air."""
|
||||||
self.raw = self._filter_for_send().rstrip("\n")
|
self.raw = "{}>APZ100:{}".format(
|
||||||
|
self.from_call,
|
||||||
|
self.payload,
|
||||||
|
)
|
||||||
|
LOG.debug(f"_build_raw: payload '{self.payload}' raw '{self.raw}'")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def factory(raw_packet):
|
def factory(raw_packet):
|
||||||
|
"""Factory method to create a packet from a raw packet string."""
|
||||||
raw = raw_packet
|
raw = raw_packet
|
||||||
raw["raw_dict"] = raw.copy()
|
raw["raw_dict"] = raw.copy()
|
||||||
translate_fields = {
|
translate_fields = {
|
||||||
@@ -118,6 +135,19 @@ class Packet(metaclass=abc.ABCMeta):
|
|||||||
packet_type = get_packet_type(raw)
|
packet_type = get_packet_type(raw)
|
||||||
raw["packet_type"] = packet_type
|
raw["packet_type"] = packet_type
|
||||||
class_name = TYPE_LOOKUP[packet_type]
|
class_name = TYPE_LOOKUP[packet_type]
|
||||||
|
if packet_type == PACKET_TYPE_THIRDPARTY:
|
||||||
|
# We have an encapsulated packet!
|
||||||
|
# So we need to decode it and return the inner packet
|
||||||
|
# as the packet we are going to process.
|
||||||
|
# This is a recursive call to the factory
|
||||||
|
subpacket_raw = raw["subpacket"]
|
||||||
|
subpacket = Packet.factory(subpacket_raw)
|
||||||
|
del raw["subpacket"]
|
||||||
|
# raw["subpacket"] = subpacket
|
||||||
|
packet = dacite.from_dict(data_class=class_name, data=raw)
|
||||||
|
packet.subpacket = subpacket
|
||||||
|
return packet
|
||||||
|
|
||||||
if packet_type == PACKET_TYPE_UNKNOWN:
|
if packet_type == PACKET_TYPE_UNKNOWN:
|
||||||
# Try and figure it out here
|
# Try and figure it out here
|
||||||
if "latitude" in raw:
|
if "latitude" in raw:
|
||||||
@@ -233,7 +263,7 @@ class PathPacket(Packet):
|
|||||||
path: List[str] = field(default_factory=list)
|
path: List[str] = field(default_factory=list)
|
||||||
via: str = None
|
via: str = None
|
||||||
|
|
||||||
def _build_raw(self):
|
def _build_payload(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
@@ -245,13 +275,8 @@ class AckPacket(PathPacket):
|
|||||||
if self.response:
|
if self.response:
|
||||||
LOG.warning("Response set!")
|
LOG.warning("Response set!")
|
||||||
|
|
||||||
def _build_raw(self):
|
def _build_payload(self):
|
||||||
"""Build the self.raw which is what is sent over the air."""
|
self.payload = f":{self.to_call.ljust(9)}:ack{self.msgNo}"
|
||||||
self.raw = "{}>APZ100::{}:ack{}".format(
|
|
||||||
self.from_call,
|
|
||||||
self.to_call.ljust(9),
|
|
||||||
self.msgNo,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -262,13 +287,8 @@ class RejectPacket(PathPacket):
|
|||||||
if self.response:
|
if self.response:
|
||||||
LOG.warning("Response set!")
|
LOG.warning("Response set!")
|
||||||
|
|
||||||
def _build_raw(self):
|
def _build_payload(self):
|
||||||
"""Build the self.raw which is what is sent over the air."""
|
self.payload = f":{self.to_call.ljust(9)} :rej{self.msgNo}"
|
||||||
self.raw = "{}>APZ100::{} :rej{}".format(
|
|
||||||
self.from_call,
|
|
||||||
self.to_call.ljust(9),
|
|
||||||
self.msgNo,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -285,10 +305,8 @@ class MessagePacket(PathPacket):
|
|||||||
# We all miss George Carlin
|
# We all miss George Carlin
|
||||||
return re.sub("fuck|shit|cunt|piss|cock|bitch", "****", message)
|
return re.sub("fuck|shit|cunt|piss|cock|bitch", "****", message)
|
||||||
|
|
||||||
def _build_raw(self):
|
def _build_payload(self):
|
||||||
"""Build the self.raw which is what is sent over the air."""
|
self.payload = ":{}:{}{{{}".format(
|
||||||
self.raw = "{}>APZ100::{}:{}{{{}".format(
|
|
||||||
self.from_call,
|
|
||||||
self.to_call.ljust(9),
|
self.to_call.ljust(9),
|
||||||
self._filter_for_send().rstrip("\n"),
|
self._filter_for_send().rstrip("\n"),
|
||||||
str(self.msgNo),
|
str(self.msgNo),
|
||||||
@@ -301,7 +319,7 @@ class StatusPacket(PathPacket):
|
|||||||
messagecapable: bool = False
|
messagecapable: bool = False
|
||||||
comment: str = None
|
comment: str = None
|
||||||
|
|
||||||
def _build_raw(self):
|
def _build_payload(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
@@ -400,16 +418,24 @@ class GPSPacket(PathPacket):
|
|||||||
time_zulu = result_utc_datetime.strftime("%d%H%M")
|
time_zulu = result_utc_datetime.strftime("%d%H%M")
|
||||||
return time_zulu
|
return time_zulu
|
||||||
|
|
||||||
def _build_raw(self):
|
def _build_payload(self):
|
||||||
|
"""The payload is the non headers portion of the packet."""
|
||||||
time_zulu = self._build_time_zulu()
|
time_zulu = self._build_time_zulu()
|
||||||
|
lat = self.latitude
|
||||||
|
long = self.longitude
|
||||||
|
self.payload = (
|
||||||
|
f"@{time_zulu}z{lat}{self.symbol_table}"
|
||||||
|
f"{long}{self.symbol}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.comment:
|
||||||
|
self.payload = f"{self.payload}{self.comment}"
|
||||||
|
|
||||||
|
def _build_raw(self):
|
||||||
self.raw = (
|
self.raw = (
|
||||||
f"{self.from_call}>{self.to_call},WIDE2-1:"
|
f"{self.from_call}>{self.to_call},WIDE2-1:"
|
||||||
f"@{time_zulu}z{self.latitude}{self.symbol_table}"
|
f"{self.payload}"
|
||||||
f"{self.longitude}{self.symbol}"
|
|
||||||
)
|
)
|
||||||
if self.comment:
|
|
||||||
self.raw = f"{self.raw}{self.comment}"
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -422,7 +448,7 @@ class MicEPacket(GPSPacket):
|
|||||||
# 0 to 360
|
# 0 to 360
|
||||||
course: int = 0
|
course: int = 0
|
||||||
|
|
||||||
def _build_raw(self):
|
def _build_payload(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
@@ -436,6 +462,19 @@ class ObjectPacket(GPSPacket):
|
|||||||
# 0 to 360
|
# 0 to 360
|
||||||
course: int = 0
|
course: int = 0
|
||||||
|
|
||||||
|
def _build_payload(self):
|
||||||
|
time_zulu = self._build_time_zulu()
|
||||||
|
lat = self.convert_latitude(self.latitude)
|
||||||
|
long = self.convert_longitude(self.longitude)
|
||||||
|
|
||||||
|
self.payload = (
|
||||||
|
f"*{time_zulu}z{lat}{self.symbol_table}"
|
||||||
|
f"{long}{self.symbol}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.comment:
|
||||||
|
self.payload = f"{self.payload}{self.comment}"
|
||||||
|
|
||||||
def _build_raw(self):
|
def _build_raw(self):
|
||||||
"""
|
"""
|
||||||
REPEAT builds packets like
|
REPEAT builds packets like
|
||||||
@@ -446,17 +485,11 @@ class ObjectPacket(GPSPacket):
|
|||||||
callsign is the station callsign for the object
|
callsign is the station callsign for the object
|
||||||
The frequency, uplink_tone, offset is part of the comment
|
The frequency, uplink_tone, offset is part of the comment
|
||||||
"""
|
"""
|
||||||
time_zulu = self._build_time_zulu()
|
|
||||||
lat = self.convert_latitude(self.latitude)
|
|
||||||
long = self.convert_longitude(self.longitude)
|
|
||||||
|
|
||||||
self.raw = (
|
self.raw = (
|
||||||
f"{self.from_call}>APZ100:;{self.to_call:9s}"
|
f"{self.from_call}>APZ100:;{self.to_call:9s}"
|
||||||
f"*{time_zulu}z{lat}{self.symbol_table}"
|
f"{self.payload}"
|
||||||
f"{long}{self.symbol}"
|
|
||||||
)
|
)
|
||||||
if self.comment:
|
|
||||||
self.raw = f"{self.raw}{self.comment}"
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass()
|
@dataclass()
|
||||||
@@ -474,7 +507,7 @@ class WeatherPacket(GPSPacket):
|
|||||||
pressure: float = 0.00
|
pressure: float = 0.00
|
||||||
comment: str = None
|
comment: str = None
|
||||||
|
|
||||||
def _build_raw(self):
|
def _build_payload(self):
|
||||||
"""Build an uncompressed weather packet
|
"""Build an uncompressed weather packet
|
||||||
|
|
||||||
Format =
|
Format =
|
||||||
@@ -502,7 +535,6 @@ class WeatherPacket(GPSPacket):
|
|||||||
time_zulu = self._build_time_zulu()
|
time_zulu = self._build_time_zulu()
|
||||||
|
|
||||||
contents = [
|
contents = [
|
||||||
f"{self.from_call}>{self.to_call},WIDE1-1,WIDE2-1:",
|
|
||||||
f"@{time_zulu}z{self.latitude}{self.symbol_table}",
|
f"@{time_zulu}z{self.latitude}{self.symbol_table}",
|
||||||
f"{self.longitude}{self.symbol}",
|
f"{self.longitude}{self.symbol}",
|
||||||
f"{self.wind_direction:03d}",
|
f"{self.wind_direction:03d}",
|
||||||
@@ -523,11 +555,32 @@ class WeatherPacket(GPSPacket):
|
|||||||
# Barometric pressure (in tenths of millibars/tenths of hPascal)
|
# Barometric pressure (in tenths of millibars/tenths of hPascal)
|
||||||
f"b{self.pressure:05.0f}",
|
f"b{self.pressure:05.0f}",
|
||||||
]
|
]
|
||||||
|
|
||||||
if self.comment:
|
if self.comment:
|
||||||
contents.append(self.comment)
|
contents.append(self.comment)
|
||||||
|
self.payload = "".join(contents)
|
||||||
|
|
||||||
self.raw = "".join(contents)
|
def _build_raw(self):
|
||||||
|
|
||||||
|
self.raw = (
|
||||||
|
f"{self.from_call}>{self.to_call},WIDE1-1,WIDE2-1:"
|
||||||
|
f"{self.payload}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ThirdParty(Packet):
|
||||||
|
# Holds the encapsulated packet
|
||||||
|
subpacket: Packet = None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""Build the repr version of the packet."""
|
||||||
|
repr_str = (
|
||||||
|
f"{self.__class__.__name__}:"
|
||||||
|
f" From: {self.from_call} "
|
||||||
|
f" To: {self.to_call} "
|
||||||
|
f" Subpacket: {repr(self.subpacket)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return repr_str
|
||||||
|
|
||||||
|
|
||||||
TYPE_LOOKUP = {
|
TYPE_LOOKUP = {
|
||||||
@@ -540,6 +593,7 @@ TYPE_LOOKUP = {
|
|||||||
PACKET_TYPE_STATUS: StatusPacket,
|
PACKET_TYPE_STATUS: StatusPacket,
|
||||||
PACKET_TYPE_BEACON: GPSPacket,
|
PACKET_TYPE_BEACON: GPSPacket,
|
||||||
PACKET_TYPE_UNKNOWN: Packet,
|
PACKET_TYPE_UNKNOWN: Packet,
|
||||||
|
PACKET_TYPE_THIRDPARTY: ThirdParty,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -566,6 +620,8 @@ def get_packet_type(packet: dict):
|
|||||||
elif pkt_format == PACKET_TYPE_UNCOMPRESSED:
|
elif pkt_format == PACKET_TYPE_UNCOMPRESSED:
|
||||||
if packet.get("symbol", None) == "_":
|
if packet.get("symbol", None) == "_":
|
||||||
packet_type = PACKET_TYPE_WX
|
packet_type = PACKET_TYPE_WX
|
||||||
|
elif pkt_format == PACKET_TYPE_THIRDPARTY:
|
||||||
|
packet_type = PACKET_TYPE_THIRDPARTY
|
||||||
return packet_type
|
return packet_type
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ def get_weather_gov_for_gps(lat, lon):
|
|||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
url2 = (
|
url2 = (
|
||||||
# "https://forecast.weather.gov/MapClick.php?lat=%s"
|
"https://forecast.weather.gov/MapClick.php?lat=%s"
|
||||||
# "&lon=%s&FcstType=json" % (lat, lon)
|
"&lon=%s&FcstType=json" % (lat, lon)
|
||||||
f"https://api.weather.gov/points/{lat},{lon}"
|
# f"https://api.weather.gov/points/{lat},{lon}"
|
||||||
)
|
)
|
||||||
LOG.debug(f"Fetching weather '{url2}'")
|
LOG.debug(f"Fetching weather '{url2}'")
|
||||||
response = requests.get(url2, headers=headers)
|
response = requests.get(url2, headers=headers)
|
||||||
|
|||||||
@@ -37,11 +37,18 @@ class USWeatherPlugin(plugin.APRSDRegexCommandPluginBase, plugin.APRSFIKEYMixin)
|
|||||||
def process(self, packet):
|
def process(self, packet):
|
||||||
LOG.info("Weather Plugin")
|
LOG.info("Weather Plugin")
|
||||||
fromcall = packet.from_call
|
fromcall = packet.from_call
|
||||||
|
message = packet.get("message_text", None)
|
||||||
# message = packet.get("message_text", None)
|
# message = packet.get("message_text", None)
|
||||||
# ack = packet.get("msgNo", "0")
|
# ack = packet.get("msgNo", "0")
|
||||||
|
a = re.search(r"^.*\s+(.*)", message)
|
||||||
|
if a is not None:
|
||||||
|
searchcall = a.group(1)
|
||||||
|
searchcall = searchcall.upper()
|
||||||
|
else:
|
||||||
|
searchcall = fromcall
|
||||||
api_key = CONF.aprs_fi.apiKey
|
api_key = CONF.aprs_fi.apiKey
|
||||||
try:
|
try:
|
||||||
aprs_data = plugin_utils.get_aprs_fi(api_key, fromcall)
|
aprs_data = plugin_utils.get_aprs_fi(api_key, searchcall)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
LOG.error(f"Failed to fetch aprs.fi data {ex}")
|
LOG.error(f"Failed to fetch aprs.fi data {ex}")
|
||||||
return "Failed to fetch aprs.fi location"
|
return "Failed to fetch aprs.fi location"
|
||||||
|
|||||||
+4
-1
@@ -18,7 +18,10 @@ LOG = logging.getLogger("APRSD")
|
|||||||
def magic_word_authenticator(sock):
|
def magic_word_authenticator(sock):
|
||||||
magic = sock.recv(len(CONF.rpc_settings.magic_word)).decode()
|
magic = sock.recv(len(CONF.rpc_settings.magic_word)).decode()
|
||||||
if magic != CONF.rpc_settings.magic_word:
|
if magic != CONF.rpc_settings.magic_word:
|
||||||
raise AuthenticationError(f"wrong magic word {magic}")
|
raise AuthenticationError(
|
||||||
|
f"wrong magic word passed in '{magic}'"
|
||||||
|
f" != '{CONF.rpc_settings.magic_word}'",
|
||||||
|
)
|
||||||
return sock, None
|
return sock, None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -246,7 +246,6 @@ class APRSDStats:
|
|||||||
},
|
},
|
||||||
"plugins": plugin_stats,
|
"plugins": plugin_stats,
|
||||||
}
|
}
|
||||||
LOG.debug(f"STATS = {stats}")
|
|
||||||
LOG.info("APRSD Stats: DONE")
|
LOG.info("APRSD Stats: DONE")
|
||||||
return stats
|
return stats
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,11 @@
|
|||||||
start_update();
|
start_update();
|
||||||
init_chat();
|
init_chat();
|
||||||
reset_Tabs();
|
reset_Tabs();
|
||||||
|
|
||||||
|
if (location.protocol != 'https:') {
|
||||||
|
// Have to disable the beacon button.
|
||||||
|
$('#send_beacon').prop('disabled', true);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
+6
-1
@@ -330,7 +330,12 @@ if __name__ == "__main__":
|
|||||||
setup_logging(app, log_level)
|
setup_logging(app, log_level)
|
||||||
sio.register_namespace(LoggingNamespace("/logs"))
|
sio.register_namespace(LoggingNamespace("/logs"))
|
||||||
CONF.log_opt_values(LOG, logging.DEBUG)
|
CONF.log_opt_values(LOG, logging.DEBUG)
|
||||||
app.run(threaded=True, debug=True, port=CONF.admin.web_port)
|
app.run(
|
||||||
|
threaded=True,
|
||||||
|
debug=False,
|
||||||
|
port=CONF.admin.web_port,
|
||||||
|
host=CONF.admin.web_ip,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "uwsgi_file_aprsd_wsgi":
|
if __name__ == "uwsgi_file_aprsd_wsgi":
|
||||||
|
|||||||
+27
-27
@@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
# pip-compile --annotation-style=line dev-requirements.in
|
# pip-compile --annotation-style=line dev-requirements.in
|
||||||
#
|
#
|
||||||
add-trailing-comma==3.0.0 # via gray
|
add-trailing-comma==3.0.1 # via gray
|
||||||
alabaster==0.7.13 # via sphinx
|
alabaster==0.7.13 # via sphinx
|
||||||
attrs==23.1.0 # via jsonschema, referencing
|
attrs==23.1.0 # via jsonschema, referencing
|
||||||
autoflake==1.5.3 # via gray
|
autoflake==1.5.3 # via gray
|
||||||
@@ -13,75 +13,75 @@ black==23.7.0 # via gray
|
|||||||
build==0.10.0 # via pip-tools
|
build==0.10.0 # via pip-tools
|
||||||
cachetools==5.3.1 # via tox
|
cachetools==5.3.1 # via tox
|
||||||
certifi==2023.7.22 # via requests
|
certifi==2023.7.22 # via requests
|
||||||
cfgv==3.3.1 # via pre-commit
|
cfgv==3.4.0 # via pre-commit
|
||||||
chardet==5.1.0 # via tox
|
chardet==5.2.0 # via tox
|
||||||
charset-normalizer==3.2.0 # via requests
|
charset-normalizer==3.2.0 # via requests
|
||||||
click==8.1.6 # via black, pip-tools
|
click==8.1.6 # via black, pip-tools
|
||||||
colorama==0.4.6 # via tox
|
colorama==0.4.6 # via tox
|
||||||
commonmark==0.9.1 # via rich
|
commonmark==0.9.1 # via rich
|
||||||
configargparse==1.7 # via gray
|
configargparse==1.7 # via gray
|
||||||
coverage[toml]==7.2.7 # via pytest-cov
|
coverage[toml]==7.3.0 # via pytest-cov
|
||||||
distlib==0.3.7 # via virtualenv
|
distlib==0.3.7 # via virtualenv
|
||||||
docutils==0.20.1 # via sphinx
|
docutils==0.20.1 # via sphinx
|
||||||
exceptiongroup==1.1.2 # via pytest
|
exceptiongroup==1.1.3 # via pytest
|
||||||
filelock==3.12.2 # via tox, virtualenv
|
filelock==3.12.2 # via tox, virtualenv
|
||||||
fixit==0.1.4 # via gray
|
fixit==0.1.4 # via gray
|
||||||
flake8==6.0.0 # via -r dev-requirements.in, fixit, pep8-naming
|
flake8==6.1.0 # via -r dev-requirements.in, fixit, pep8-naming
|
||||||
gray==0.13.0 # via -r dev-requirements.in
|
gray==0.13.0 # via -r dev-requirements.in
|
||||||
identify==2.5.26 # via pre-commit
|
identify==2.5.26 # via pre-commit
|
||||||
idna==3.4 # via requests
|
idna==3.4 # via requests
|
||||||
imagesize==1.4.1 # via sphinx
|
imagesize==1.4.1 # via sphinx
|
||||||
importlib-resources==6.0.0 # via fixit
|
importlib-resources==6.0.1 # via fixit
|
||||||
iniconfig==2.0.0 # via pytest
|
iniconfig==2.0.0 # via pytest
|
||||||
isort==5.12.0 # via -r dev-requirements.in, gray
|
isort==5.12.0 # via -r dev-requirements.in, gray
|
||||||
jinja2==3.1.2 # via sphinx
|
jinja2==3.1.2 # via sphinx
|
||||||
jsonschema==4.18.4 # via fixit
|
jsonschema==4.19.0 # via fixit
|
||||||
jsonschema-specifications==2023.7.1 # via jsonschema
|
jsonschema-specifications==2023.7.1 # via jsonschema
|
||||||
libcst==1.0.1 # via fixit
|
libcst==1.0.1 # via fixit
|
||||||
markupsafe==2.1.3 # via jinja2
|
markupsafe==2.1.3 # via jinja2
|
||||||
mccabe==0.7.0 # via flake8
|
mccabe==0.7.0 # via flake8
|
||||||
mypy==1.4.1 # via -r dev-requirements.in
|
mypy==1.5.0 # via -r dev-requirements.in
|
||||||
mypy-extensions==1.0.0 # via black, mypy, typing-inspect
|
mypy-extensions==1.0.0 # via black, mypy, typing-inspect
|
||||||
nodeenv==1.8.0 # via pre-commit
|
nodeenv==1.8.0 # via pre-commit
|
||||||
packaging==23.1 # via black, build, pyproject-api, pytest, sphinx, tox
|
packaging==23.1 # via black, build, pyproject-api, pytest, sphinx, tox
|
||||||
pathspec==0.11.1 # via black
|
pathspec==0.11.2 # via black
|
||||||
pep8-naming==0.13.3 # via -r dev-requirements.in
|
pep8-naming==0.13.3 # via -r dev-requirements.in
|
||||||
pip-tools==7.1.0 # via -r dev-requirements.in
|
pip-tools==7.3.0 # via -r dev-requirements.in
|
||||||
platformdirs==3.9.1 # via black, tox, virtualenv
|
platformdirs==3.10.0 # via black, tox, virtualenv
|
||||||
pluggy==1.2.0 # via pytest, tox
|
pluggy==1.2.0 # via pytest, tox
|
||||||
pre-commit==3.3.3 # via -r dev-requirements.in
|
pre-commit==3.3.3 # via -r dev-requirements.in
|
||||||
pycodestyle==2.10.0 # via flake8
|
pycodestyle==2.11.0 # via flake8
|
||||||
pyflakes==3.0.1 # via autoflake, flake8
|
pyflakes==3.1.0 # via autoflake, flake8
|
||||||
pygments==2.15.1 # via rich, sphinx
|
pygments==2.16.1 # via rich, sphinx
|
||||||
pyproject-api==1.5.3 # via tox
|
pyproject-api==1.5.3 # via tox
|
||||||
pyproject-hooks==1.0.0 # via build
|
pyproject-hooks==1.0.0 # via build
|
||||||
pytest==7.4.0 # via -r dev-requirements.in, pytest-cov
|
pytest==7.4.0 # via -r dev-requirements.in, pytest-cov
|
||||||
pytest-cov==4.1.0 # via -r dev-requirements.in
|
pytest-cov==4.1.0 # via -r dev-requirements.in
|
||||||
pyupgrade==3.9.0 # via gray
|
pyupgrade==3.10.1 # via gray
|
||||||
pyyaml==6.0.1 # via fixit, libcst, pre-commit
|
pyyaml==6.0.1 # via fixit, libcst, pre-commit
|
||||||
referencing==0.30.0 # via jsonschema, jsonschema-specifications
|
referencing==0.30.2 # via jsonschema, jsonschema-specifications
|
||||||
requests==2.31.0 # via sphinx
|
requests==2.31.0 # via sphinx
|
||||||
rich==12.6.0 # via gray
|
rich==12.6.0 # via gray
|
||||||
rpds-py==0.9.2 # via jsonschema, referencing
|
rpds-py==0.9.2 # via jsonschema, referencing
|
||||||
snowballstemmer==2.2.0 # via sphinx
|
snowballstemmer==2.2.0 # via sphinx
|
||||||
sphinx==7.0.1 # via -r dev-requirements.in
|
sphinx==7.1.2 # via -r dev-requirements.in, sphinxcontrib-applehelp, sphinxcontrib-devhelp, sphinxcontrib-htmlhelp, sphinxcontrib-qthelp, sphinxcontrib-serializinghtml
|
||||||
sphinxcontrib-applehelp==1.0.4 # via sphinx
|
sphinxcontrib-applehelp==1.0.7 # via sphinx
|
||||||
sphinxcontrib-devhelp==1.0.2 # via sphinx
|
sphinxcontrib-devhelp==1.0.5 # via sphinx
|
||||||
sphinxcontrib-htmlhelp==2.0.1 # via sphinx
|
sphinxcontrib-htmlhelp==2.0.4 # via sphinx
|
||||||
sphinxcontrib-jsmath==1.0.1 # via sphinx
|
sphinxcontrib-jsmath==1.0.1 # via sphinx
|
||||||
sphinxcontrib-qthelp==1.0.3 # via sphinx
|
sphinxcontrib-qthelp==1.0.6 # via sphinx
|
||||||
sphinxcontrib-serializinghtml==1.1.5 # via sphinx
|
sphinxcontrib-serializinghtml==1.1.8 # via sphinx
|
||||||
tokenize-rt==5.1.0 # via add-trailing-comma, pyupgrade
|
tokenize-rt==5.2.0 # via add-trailing-comma, pyupgrade
|
||||||
toml==0.10.2 # via autoflake
|
toml==0.10.2 # via autoflake
|
||||||
tomli==2.0.1 # via black, build, coverage, mypy, pip-tools, pyproject-api, pyproject-hooks, pytest, tox
|
tomli==2.0.1 # via black, build, coverage, mypy, pip-tools, pyproject-api, pyproject-hooks, pytest, tox
|
||||||
tox==4.6.4 # via -r dev-requirements.in
|
tox==4.8.0 # via -r dev-requirements.in
|
||||||
typing-extensions==4.7.1 # via libcst, mypy, typing-inspect
|
typing-extensions==4.7.1 # via libcst, mypy, typing-inspect
|
||||||
typing-inspect==0.9.0 # via libcst
|
typing-inspect==0.9.0 # via libcst
|
||||||
unify==0.5 # via gray
|
unify==0.5 # via gray
|
||||||
untokenize==0.1.1 # via unify
|
untokenize==0.1.1 # via unify
|
||||||
urllib3==2.0.4 # via requests
|
urllib3==2.0.4 # via requests
|
||||||
virtualenv==20.24.1 # via pre-commit, tox
|
virtualenv==20.24.3 # via pre-commit, tox
|
||||||
wheel==0.41.0 # via pip-tools
|
wheel==0.41.1 # via pip-tools
|
||||||
|
|
||||||
# The following packages are considered to be unsafe in a requirements file:
|
# The following packages are considered to be unsafe in a requirements file:
|
||||||
# pip
|
# pip
|
||||||
|
|||||||
+44
-45
@@ -1,62 +1,61 @@
|
|||||||
#FROM python:3-bullseye as aprsd
|
FROM python:3.11-slim as build
|
||||||
FROM ubuntu:22.04 as aprsd
|
|
||||||
|
|
||||||
# Dockerfile for building a container during aprsd development.
|
ARG VERSION=3.1.0
|
||||||
|
|
||||||
ARG UID
|
|
||||||
ARG GID
|
|
||||||
ARG TZ
|
|
||||||
ARG VERSION=3.0.3
|
|
||||||
ARG BUILDX_QEMU_ENV
|
|
||||||
ENV APRS_USER=aprs
|
|
||||||
ENV HOME=/home/aprs
|
|
||||||
ENV TZ=${TZ:-US/Eastern}
|
ENV TZ=${TZ:-US/Eastern}
|
||||||
ENV UID=${UID:-1000}
|
|
||||||
ENV GID=${GID:-1000}
|
|
||||||
ENV LC_ALL=C.UTF-8
|
ENV LC_ALL=C.UTF-8
|
||||||
ENV LANG=C.UTF-8
|
ENV LANG=C.UTF-8
|
||||||
ENV APRSD_PIP_VERSION=${VERSION}
|
ENV APRSD_PIP_VERSION=${VERSION}
|
||||||
|
|
||||||
|
ENV PIP_DEFAULT_TIMEOUT=100 \
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
# Allow statements and log messages to immediately appear
|
||||||
RUN apt update
|
PYTHONUNBUFFERED=1 \
|
||||||
RUN apt install -y git build-essential
|
# disable a pip version check to reduce run-time & log-spam
|
||||||
RUN apt install -y libffi-dev python3-dev libssl-dev libxml2-dev libxslt-dev
|
PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
||||||
RUN apt install -y python3 python3-pip python3-dev python3-lxml python3-setuptools-rust
|
# cache is useless in docker image, so disable to reduce image size
|
||||||
RUN apt install -y libffi-dev cargo pkg-config
|
PIP_NO_CACHE_DIR=1
|
||||||
|
|
||||||
RUN pip3 install -U pip
|
|
||||||
RUN pip3 install -U setuptools_rust
|
|
||||||
|
|
||||||
|
|
||||||
RUN addgroup --gid $GID $APRS_USER
|
RUN set -ex \
|
||||||
RUN useradd -m -u $UID -g $APRS_USER $APRS_USER
|
# Create a non-root user
|
||||||
|
&& addgroup --system --gid 1001 appgroup \
|
||||||
|
&& useradd --uid 1001 --gid 1001 -s /usr/bin/bash -m -d /app appuser \
|
||||||
|
# Upgrade the package index and install security upgrades
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get upgrade -y \
|
||||||
|
&& apt-get install -y git build-essential curl vim libffi-dev \
|
||||||
|
python3-dev libssl-dev libxml2-dev libxslt-dev telnet sudo \
|
||||||
|
# Install dependencies
|
||||||
|
# Clean up
|
||||||
|
&& apt-get autoremove -y \
|
||||||
|
&& apt-get clean -y
|
||||||
|
|
||||||
# Handle an extremely specific issue when building the cryptography package for
|
|
||||||
# 32-bit architectures within QEMU running on a 64-bit host (issue #30).
|
|
||||||
RUN if [ "${BUILDX_QEMU_ENV}" = "true" -a "$(getconf LONG_BIT)" = "32" ]; then \
|
|
||||||
pip3 install -U cryptography==3.3.2; \
|
|
||||||
else \
|
|
||||||
pip3 install cryptography ;\
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Ensure /config is there with a default config file
|
### Final stage
|
||||||
USER root
|
FROM build as final
|
||||||
# Install aprsd
|
WORKDIR /app
|
||||||
|
|
||||||
RUN pip3 install aprsd==$APRSD_PIP_VERSION
|
RUN pip3 install aprsd==$APRSD_PIP_VERSION
|
||||||
RUN mkdir -p /config
|
RUN pip install gevent uwsgi
|
||||||
|
RUN which aprsd
|
||||||
|
RUN mkdir /config
|
||||||
|
RUN chown -R appuser:appgroup /app
|
||||||
|
RUN chown -R appuser:appgroup /config
|
||||||
|
USER appuser
|
||||||
|
RUN which aprsd
|
||||||
RUN aprsd sample-config > /config/aprsd.conf
|
RUN aprsd sample-config > /config/aprsd.conf
|
||||||
RUN chown -R $APRS_USER:$APRS_USER /config
|
|
||||||
RUN chown -R $APRS_USER:$APRS_USER $HOME
|
|
||||||
|
|
||||||
# override this to run another configuration
|
ADD bin/run.sh /app
|
||||||
ENV CONF default
|
ADD bin/listen.sh /app
|
||||||
VOLUME ["/config", "/plugins"]
|
ADD bin/admin.sh /app
|
||||||
|
|
||||||
USER $APRS_USER
|
# For the web admin interface
|
||||||
ADD bin/run.sh /usr/local/bin
|
EXPOSE 8001
|
||||||
ADD bin/listen.sh /usr/local/bin
|
|
||||||
ENTRYPOINT ["/usr/local/bin/run.sh"]
|
ENTRYPOINT ["/app/run.sh"]
|
||||||
|
VOLUME ["/config"]
|
||||||
|
|
||||||
|
# Set the user to run the application
|
||||||
|
USER appuser
|
||||||
|
|
||||||
HEALTHCHECK --interval=5m --timeout=12s --start-period=30s \
|
HEALTHCHECK --interval=5m --timeout=12s --start-period=30s \
|
||||||
CMD aprsd healthcheck --config /config/aprsd.conf
|
CMD aprsd healthcheck --config /config/aprsd.conf
|
||||||
|
|||||||
@@ -29,14 +29,12 @@ kiss3
|
|||||||
attrs
|
attrs
|
||||||
# for mobile checking
|
# for mobile checking
|
||||||
user-agents
|
user-agents
|
||||||
pyopenssl
|
|
||||||
dataclasses
|
dataclasses
|
||||||
dacite2
|
dacite2
|
||||||
oslo.config
|
oslo.config
|
||||||
rpyc
|
rpyc
|
||||||
# Pin this here so it doesn't require a compile on
|
# Pin this here so it doesn't require a compile on
|
||||||
# raspi
|
# raspi
|
||||||
cryptography
|
|
||||||
shellingham
|
shellingham
|
||||||
geopy
|
geopy
|
||||||
rush
|
rush
|
||||||
|
|||||||
+7
-16
@@ -4,39 +4,33 @@
|
|||||||
#
|
#
|
||||||
# pip-compile --annotation-style=line requirements.in
|
# pip-compile --annotation-style=line requirements.in
|
||||||
#
|
#
|
||||||
anyio==3.7.1 # via httpcore
|
|
||||||
aprslib==0.7.2 # via -r requirements.in
|
aprslib==0.7.2 # via -r requirements.in
|
||||||
attrs==23.1.0 # via -r requirements.in, ax253, kiss3, rush
|
attrs==23.1.0 # via -r requirements.in, ax253, kiss3, rush
|
||||||
ax253==0.1.5.post1 # via kiss3
|
ax253==0.1.5.post1 # via kiss3
|
||||||
beautifulsoup4==4.12.2 # via -r requirements.in
|
beautifulsoup4==4.12.2 # via -r requirements.in
|
||||||
bidict==0.22.1 # via python-socketio
|
bidict==0.22.1 # via python-socketio
|
||||||
bitarray==2.8.0 # via ax253, kiss3
|
bitarray==2.8.1 # via ax253, kiss3
|
||||||
blinker==1.6.2 # via flask
|
blinker==1.6.2 # via flask
|
||||||
certifi==2023.7.22 # via httpcore, requests
|
certifi==2023.7.22 # via requests
|
||||||
cffi==1.15.1 # via cryptography
|
|
||||||
charset-normalizer==3.2.0 # via requests
|
charset-normalizer==3.2.0 # via requests
|
||||||
click==8.1.6 # via -r requirements.in, click-completion, click-params, flask
|
click==8.1.6 # via -r requirements.in, click-completion, click-params, flask
|
||||||
click-completion==0.5.2 # via -r requirements.in
|
click-completion==0.5.2 # via -r requirements.in
|
||||||
click-params==0.4.1 # via -r requirements.in
|
click-params==0.4.1 # via -r requirements.in
|
||||||
commonmark==0.9.1 # via rich
|
commonmark==0.9.1 # via rich
|
||||||
cryptography==41.0.2 # via -r requirements.in, pyopenssl
|
|
||||||
dacite2==2.0.0 # via -r requirements.in
|
dacite2==2.0.0 # via -r requirements.in
|
||||||
dataclasses==0.6 # via -r requirements.in
|
dataclasses==0.6 # via -r requirements.in
|
||||||
debtcollector==2.5.0 # via oslo-config
|
debtcollector==2.5.0 # via oslo-config
|
||||||
decorator==5.1.1 # via validators
|
decorator==5.1.1 # via validators
|
||||||
dnspython==2.4.0 # via eventlet
|
dnspython==2.4.2 # via eventlet
|
||||||
eventlet==0.33.3 # via -r requirements.in
|
eventlet==0.33.3 # via -r requirements.in
|
||||||
exceptiongroup==1.1.2 # via anyio
|
|
||||||
flask==2.3.2 # via -r requirements.in, flask-httpauth, flask-socketio
|
flask==2.3.2 # via -r requirements.in, flask-httpauth, flask-socketio
|
||||||
flask-httpauth==4.8.0 # via -r requirements.in
|
flask-httpauth==4.8.0 # via -r requirements.in
|
||||||
flask-socketio==5.3.4 # via -r requirements.in
|
flask-socketio==5.3.5 # via -r requirements.in
|
||||||
geographiclib==2.0 # via geopy
|
geographiclib==2.0 # via geopy
|
||||||
geopy==2.3.0 # via -r requirements.in
|
geopy==2.3.0 # via -r requirements.in
|
||||||
gevent==23.7.0 # via -r requirements.in
|
gevent==23.7.0 # via -r requirements.in
|
||||||
greenlet==2.0.2 # via eventlet, gevent
|
greenlet==2.0.2 # via eventlet, gevent
|
||||||
h11==0.14.0 # via httpcore
|
idna==3.4 # via requests
|
||||||
httpcore==0.17.3 # via dnspython
|
|
||||||
idna==3.4 # via anyio, requests
|
|
||||||
imapclient==2.3.1 # via -r requirements.in
|
imapclient==2.3.1 # via -r requirements.in
|
||||||
importlib-metadata==6.8.0 # via ax253, kiss3
|
importlib-metadata==6.8.0 # via ax253, kiss3
|
||||||
itsdangerous==2.1.2 # via flask
|
itsdangerous==2.1.2 # via flask
|
||||||
@@ -49,9 +43,7 @@ oslo-i18n==6.0.0 # via oslo-config
|
|||||||
pbr==5.11.1 # via -r requirements.in, oslo-i18n, stevedore
|
pbr==5.11.1 # via -r requirements.in, oslo-i18n, stevedore
|
||||||
pluggy==1.2.0 # via -r requirements.in
|
pluggy==1.2.0 # via -r requirements.in
|
||||||
plumbum==1.8.2 # via rpyc
|
plumbum==1.8.2 # via rpyc
|
||||||
pycparser==2.21 # via cffi
|
pygments==2.16.1 # via rich
|
||||||
pygments==2.15.1 # via rich
|
|
||||||
pyopenssl==23.2.0 # via -r requirements.in
|
|
||||||
pyserial==3.5 # via pyserial-asyncio
|
pyserial==3.5 # via pyserial-asyncio
|
||||||
pyserial-asyncio==0.6 # via kiss3
|
pyserial-asyncio==0.6 # via kiss3
|
||||||
python-engineio==4.5.1 # via python-socketio
|
python-engineio==4.5.1 # via python-socketio
|
||||||
@@ -65,7 +57,6 @@ rpyc==5.3.1 # via -r requirements.in
|
|||||||
rush==2021.4.0 # via -r requirements.in
|
rush==2021.4.0 # via -r requirements.in
|
||||||
shellingham==1.5.0.post1 # via -r requirements.in, click-completion
|
shellingham==1.5.0.post1 # via -r requirements.in, click-completion
|
||||||
six==1.16.0 # via -r requirements.in, click-completion, eventlet, imapclient
|
six==1.16.0 # via -r requirements.in, click-completion, eventlet, imapclient
|
||||||
sniffio==1.3.0 # via anyio, dnspython, httpcore
|
|
||||||
soupsieve==2.4.1 # via beautifulsoup4
|
soupsieve==2.4.1 # via beautifulsoup4
|
||||||
stevedore==5.1.0 # via oslo-config
|
stevedore==5.1.0 # via oslo-config
|
||||||
tabulate==0.9.0 # via -r requirements.in
|
tabulate==0.9.0 # via -r requirements.in
|
||||||
@@ -75,7 +66,7 @@ update-checker==0.18.0 # via -r requirements.in
|
|||||||
urllib3==2.0.4 # via requests
|
urllib3==2.0.4 # via requests
|
||||||
user-agents==2.2.0 # via -r requirements.in
|
user-agents==2.2.0 # via -r requirements.in
|
||||||
validators==0.20.0 # via click-params
|
validators==0.20.0 # via click-params
|
||||||
werkzeug==2.3.6 # via -r requirements.in, flask
|
werkzeug==2.3.7 # via -r requirements.in, flask
|
||||||
wrapt==1.15.0 # via -r requirements.in, debtcollector
|
wrapt==1.15.0 # via -r requirements.in, debtcollector
|
||||||
zipp==3.16.2 # via importlib-metadata
|
zipp==3.16.2 # via importlib-metadata
|
||||||
zope-event==5.0 # via gevent
|
zope-event==5.0 # via gevent
|
||||||
|
|||||||
Reference in New Issue
Block a user