mirror of
https://github.com/craigerl/aprsd.git
synced 2025-06-18 14:22:32 -04:00
Lots of fixes
This commit is contained in:
parent
4c0150dd97
commit
231c15b1af
@ -1,29 +1,48 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v3.2.0
|
rev: v3.4.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
- id: check-yaml
|
- id: check-yaml
|
||||||
- id: check-added-large-files
|
- id: check-added-large-files
|
||||||
- id: fix-encoding-pragma
|
- id: detect-private-key
|
||||||
- id: detect-private-key
|
- id: check-merge-conflict
|
||||||
- id: check-merge-conflict
|
- id: check-case-conflict
|
||||||
- id: check-case-conflict
|
- id: check-docstring-first
|
||||||
- id: check-docstring-first
|
- id: check-builtin-literals
|
||||||
- id: check-builtin-literals
|
- id: double-quote-string-fixer
|
||||||
- repo: https://github.com/psf/black
|
|
||||||
rev: 19.3b0
|
|
||||||
hooks:
|
|
||||||
- id: black
|
|
||||||
|
|
||||||
- repo: https://github.com/pre-commit/mirrors-isort
|
- repo: https://github.com/asottile/setup-cfg-fmt
|
||||||
rev: v5.7.0
|
rev: v1.16.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: isort
|
- id: setup-cfg-fmt
|
||||||
|
|
||||||
- repo: https://gitlab.com/pycqa/flake8
|
- repo: https://github.com/asottile/add-trailing-comma
|
||||||
rev: 3.8.1
|
rev: v2.0.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: add-trailing-comma
|
||||||
additional_dependencies: [flake8-bugbear]
|
args: [--py36-plus]
|
||||||
|
|
||||||
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
|
rev: v2.7.4
|
||||||
|
hooks:
|
||||||
|
- id: pyupgrade
|
||||||
|
args:
|
||||||
|
- --py3-plus
|
||||||
|
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-isort
|
||||||
|
rev: v5.7.0
|
||||||
|
hooks:
|
||||||
|
- id: isort
|
||||||
|
|
||||||
|
- repo: https://github.com/psf/black
|
||||||
|
rev: 20.8b1
|
||||||
|
hooks:
|
||||||
|
- id: black
|
||||||
|
|
||||||
|
- repo: https://gitlab.com/pycqa/flake8
|
||||||
|
rev: 3.8.4
|
||||||
|
hooks:
|
||||||
|
- id: flake8
|
||||||
|
additional_dependencies: [flake8-bugbear]
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
# not use this file except in compliance with the License. You may obtain
|
# not use this file except in compliance with the License. You may obtain
|
||||||
# a copy of the License at
|
# a copy of the License at
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import logging
|
import logging
|
||||||
import select
|
import select
|
||||||
import socket
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import aprslib
|
import aprslib
|
||||||
@ -9,7 +7,7 @@ import aprslib
|
|||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
|
||||||
|
|
||||||
class Client(object):
|
class Client:
|
||||||
"""Singleton client class that constructs the aprslib connection."""
|
"""Singleton client class that constructs the aprslib connection."""
|
||||||
|
|
||||||
_instance = None
|
_instance = None
|
||||||
@ -19,7 +17,7 @@ class Client(object):
|
|||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
"""This magic turns this into a singleton."""
|
"""This magic turns this into a singleton."""
|
||||||
if cls._instance is None:
|
if cls._instance is None:
|
||||||
cls._instance = super(Client, cls).__new__(cls)
|
cls._instance = super().__new__(cls)
|
||||||
# Put any initialization here.
|
# Put any initialization here.
|
||||||
return cls._instance
|
return cls._instance
|
||||||
|
|
||||||
@ -82,7 +80,7 @@ class Aprsdis(aprslib.IS):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.sock.setblocking(0)
|
self.sock.setblocking(0)
|
||||||
except socket.error as e:
|
except OSError as e:
|
||||||
self.logger.error("socket error when setblocking(0): %s" % str(e))
|
self.logger.error("socket error when setblocking(0): %s" % str(e))
|
||||||
raise aprslib.ConnectionDrop("connection dropped")
|
raise aprslib.ConnectionDrop("connection dropped")
|
||||||
|
|
||||||
@ -93,7 +91,10 @@ class Aprsdis(aprslib.IS):
|
|||||||
# set a select timeout, so we get a chance to exit
|
# set a select timeout, so we get a chance to exit
|
||||||
# when user hits CTRL-C
|
# when user hits CTRL-C
|
||||||
readable, writable, exceptional = select.select(
|
readable, writable, exceptional = select.select(
|
||||||
[self.sock], [], [], self.select_timeout
|
[self.sock],
|
||||||
|
[],
|
||||||
|
[],
|
||||||
|
self.select_timeout,
|
||||||
)
|
)
|
||||||
if not readable:
|
if not readable:
|
||||||
continue
|
continue
|
||||||
@ -105,7 +106,7 @@ class Aprsdis(aprslib.IS):
|
|||||||
if not short_buf:
|
if not short_buf:
|
||||||
self.logger.error("socket.recv(): returned empty")
|
self.logger.error("socket.recv(): returned empty")
|
||||||
raise aprslib.ConnectionDrop("connection dropped")
|
raise aprslib.ConnectionDrop("connection dropped")
|
||||||
except socket.error as e:
|
except OSError as e:
|
||||||
# self.logger.error("socket error on recv(): %s" % str(e))
|
# self.logger.error("socket error on recv(): %s" % str(e))
|
||||||
if "Resource temporarily unavailable" in str(e):
|
if "Resource temporarily unavailable" in str(e):
|
||||||
if not blocking:
|
if not blocking:
|
||||||
|
@ -1,18 +1,15 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import datetime
|
import datetime
|
||||||
import email
|
import email
|
||||||
|
from email.mime.text import MIMEText
|
||||||
import imaplib
|
import imaplib
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import smtplib
|
import smtplib
|
||||||
import time
|
import time
|
||||||
from email.mime.text import MIMEText
|
|
||||||
|
|
||||||
import imapclient
|
|
||||||
import six
|
|
||||||
from validate_email import validate_email
|
|
||||||
|
|
||||||
from aprsd import messaging, threads
|
from aprsd import messaging, threads
|
||||||
|
import imapclient
|
||||||
|
from validate_email import validate_email
|
||||||
|
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
|
||||||
@ -30,7 +27,10 @@ def _imap_connect():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
server = imapclient.IMAPClient(
|
server = imapclient.IMAPClient(
|
||||||
CONFIG["imap"]["host"], port=imap_port, use_uid=True, ssl=use_ssl
|
CONFIG["imap"]["host"],
|
||||||
|
port=imap_port,
|
||||||
|
use_uid=True,
|
||||||
|
ssl=use_ssl,
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.error("Failed to connect IMAP server")
|
LOG.error("Failed to connect IMAP server")
|
||||||
@ -53,7 +53,7 @@ def _smtp_connect():
|
|||||||
use_ssl = CONFIG["smtp"].get("use_ssl", False)
|
use_ssl = CONFIG["smtp"].get("use_ssl", False)
|
||||||
msg = "{}{}:{}".format("SSL " if use_ssl else "", host, smtp_port)
|
msg = "{}{}:{}".format("SSL " if use_ssl else "", host, smtp_port)
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
"Connect to SMTP host {} with user '{}'".format(msg, CONFIG["imap"]["login"])
|
"Connect to SMTP host {} with user '{}'".format(msg, CONFIG["imap"]["login"]),
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -84,7 +84,7 @@ def validate_shortcuts(config):
|
|||||||
|
|
||||||
LOG.info(
|
LOG.info(
|
||||||
"Validating {} Email shortcuts. This can take up to 10 seconds"
|
"Validating {} Email shortcuts. This can take up to 10 seconds"
|
||||||
" per shortcut".format(len(shortcuts))
|
" per shortcut".format(len(shortcuts)),
|
||||||
)
|
)
|
||||||
delete_keys = []
|
delete_keys = []
|
||||||
for key in shortcuts:
|
for key in shortcuts:
|
||||||
@ -102,8 +102,8 @@ def validate_shortcuts(config):
|
|||||||
if not is_valid:
|
if not is_valid:
|
||||||
LOG.error(
|
LOG.error(
|
||||||
"'{}' is an invalid email address. Removing shortcut".format(
|
"'{}' is an invalid email address. Removing shortcut".format(
|
||||||
shortcuts[key]
|
shortcuts[key],
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
delete_keys.append(key)
|
delete_keys.append(key)
|
||||||
|
|
||||||
@ -173,14 +173,18 @@ def parse_email(msgid, data, server):
|
|||||||
|
|
||||||
if part.get_content_type() == "text/plain":
|
if part.get_content_type() == "text/plain":
|
||||||
LOG.debug("Email got text/plain")
|
LOG.debug("Email got text/plain")
|
||||||
text = six.text_type(
|
text = str(
|
||||||
part.get_payload(decode=True), str(charset), "ignore"
|
part.get_payload(decode=True),
|
||||||
|
str(charset),
|
||||||
|
"ignore",
|
||||||
).encode("utf8", "replace")
|
).encode("utf8", "replace")
|
||||||
|
|
||||||
if part.get_content_type() == "text/html":
|
if part.get_content_type() == "text/html":
|
||||||
LOG.debug("Email got text/html")
|
LOG.debug("Email got text/html")
|
||||||
html = six.text_type(
|
html = str(
|
||||||
part.get_payload(decode=True), str(charset), "ignore"
|
part.get_payload(decode=True),
|
||||||
|
str(charset),
|
||||||
|
"ignore",
|
||||||
).encode("utf8", "replace")
|
).encode("utf8", "replace")
|
||||||
|
|
||||||
if text is not None:
|
if text is not None:
|
||||||
@ -192,12 +196,15 @@ def parse_email(msgid, data, server):
|
|||||||
# email.uscc.net sends no charset, blows up unicode function below
|
# email.uscc.net sends no charset, blows up unicode function below
|
||||||
LOG.debug("Email is not multipart")
|
LOG.debug("Email is not multipart")
|
||||||
if msg.get_content_charset() is None:
|
if msg.get_content_charset() is None:
|
||||||
text = six.text_type(
|
text = str(msg.get_payload(decode=True), "US-ASCII", "ignore").encode(
|
||||||
msg.get_payload(decode=True), "US-ASCII", "ignore"
|
"utf8",
|
||||||
).encode("utf8", "replace")
|
"replace",
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
text = six.text_type(
|
text = str(
|
||||||
msg.get_payload(decode=True), msg.get_content_charset(), "ignore"
|
msg.get_payload(decode=True),
|
||||||
|
msg.get_content_charset(),
|
||||||
|
"ignore",
|
||||||
).encode("utf8", "replace")
|
).encode("utf8", "replace")
|
||||||
body = text.strip()
|
body = text.strip()
|
||||||
|
|
||||||
@ -266,11 +273,11 @@ def resend_email(count, fromcall):
|
|||||||
month = date.strftime("%B")[:3] # Nov, Mar, Apr
|
month = date.strftime("%B")[:3] # Nov, Mar, Apr
|
||||||
day = date.day
|
day = date.day
|
||||||
year = date.year
|
year = date.year
|
||||||
today = "%s-%s-%s" % (day, month, year)
|
today = "{}-{}-{}".format(day, month, year)
|
||||||
|
|
||||||
shortcuts = CONFIG["shortcuts"]
|
shortcuts = CONFIG["shortcuts"]
|
||||||
# swap key/value
|
# swap key/value
|
||||||
shortcuts_inverted = dict([[v, k] for k, v in shortcuts.items()])
|
shortcuts_inverted = {v: k for k, v in shortcuts.items()}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
server = _imap_connect()
|
server = _imap_connect()
|
||||||
@ -310,7 +317,7 @@ def resend_email(count, fromcall):
|
|||||||
# thinking this is a duplicate message.
|
# thinking this is a duplicate message.
|
||||||
# The FT1XDR pretty much ignores the aprs message number in this
|
# The FT1XDR pretty much ignores the aprs message number in this
|
||||||
# regard. The FTM400 gets it right.
|
# regard. The FTM400 gets it right.
|
||||||
reply = "No new msg %s:%s:%s" % (
|
reply = "No new msg {}:{}:{}".format(
|
||||||
str(h).zfill(2),
|
str(h).zfill(2),
|
||||||
str(m).zfill(2),
|
str(m).zfill(2),
|
||||||
str(s).zfill(2),
|
str(s).zfill(2),
|
||||||
@ -328,7 +335,7 @@ def resend_email(count, fromcall):
|
|||||||
|
|
||||||
class APRSDEmailThread(threads.APRSDThread):
|
class APRSDEmailThread(threads.APRSDThread):
|
||||||
def __init__(self, msg_queues, config):
|
def __init__(self, msg_queues, config):
|
||||||
super(APRSDEmailThread, self).__init__("EmailThread")
|
super().__init__("EmailThread")
|
||||||
self.msg_queues = msg_queues
|
self.msg_queues = msg_queues
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
@ -354,13 +361,13 @@ class APRSDEmailThread(threads.APRSDThread):
|
|||||||
|
|
||||||
shortcuts = CONFIG["shortcuts"]
|
shortcuts = CONFIG["shortcuts"]
|
||||||
# swap key/value
|
# swap key/value
|
||||||
shortcuts_inverted = dict([[v, k] for k, v in shortcuts.items()])
|
shortcuts_inverted = {v: k for k, v in shortcuts.items()}
|
||||||
|
|
||||||
date = datetime.datetime.now()
|
date = datetime.datetime.now()
|
||||||
month = date.strftime("%B")[:3] # Nov, Mar, Apr
|
month = date.strftime("%B")[:3] # Nov, Mar, Apr
|
||||||
day = date.day
|
day = date.day
|
||||||
year = date.year
|
year = date.year
|
||||||
today = "%s-%s-%s" % (day, month, year)
|
today = "{}-{}-{}".format(day, month, year)
|
||||||
|
|
||||||
server = None
|
server = None
|
||||||
try:
|
try:
|
||||||
@ -378,7 +385,8 @@ class APRSDEmailThread(threads.APRSDThread):
|
|||||||
envelope = data[b"ENVELOPE"]
|
envelope = data[b"ENVELOPE"]
|
||||||
# LOG.debug('ID:%d "%s" (%s)' % (msgid, envelope.subject.decode(), envelope.date))
|
# LOG.debug('ID:%d "%s" (%s)' % (msgid, envelope.subject.decode(), envelope.date))
|
||||||
f = re.search(
|
f = re.search(
|
||||||
r"'([[A-a][0-9]_-]+@[[A-a][0-9]_-\.]+)", str(envelope.from_[0])
|
r"'([[A-a][0-9]_-]+@[[A-a][0-9]_-\.]+)",
|
||||||
|
str(envelope.from_[0]),
|
||||||
)
|
)
|
||||||
if f is not None:
|
if f is not None:
|
||||||
from_addr = f.group(1)
|
from_addr = f.group(1)
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
|
from logging.handlers import RotatingFileHandler
|
||||||
import socketserver
|
import socketserver
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from logging.handlers import RotatingFileHandler
|
|
||||||
|
|
||||||
from aprsd import utils
|
from aprsd import utils
|
||||||
|
|
||||||
@ -74,7 +73,7 @@ def main():
|
|||||||
|
|
||||||
ip = CONFIG["aprs"]["host"]
|
ip = CONFIG["aprs"]["host"]
|
||||||
port = CONFIG["aprs"]["port"]
|
port = CONFIG["aprs"]["port"]
|
||||||
LOG.info("Start server listening on %s:%s" % (args.ip, args.port))
|
LOG.info("Start server listening on {}:{}".format(args.ip, args.port))
|
||||||
|
|
||||||
with socketserver.TCPServer((ip, port), MyAPRSTCPHandler) as server:
|
with socketserver.TCPServer((ip, port), MyAPRSTCPHandler) as server:
|
||||||
server.serve_forever()
|
server.serve_forever()
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
#
|
||||||
# @author Sinu John
|
# @author Sinu John
|
||||||
# sinuvian at gmail dot com
|
# sinuvian at gmail dot com
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
#
|
||||||
# Listen on amateur radio aprs-is network for messages and respond to them.
|
# Listen on amateur radio aprs-is network for messages and respond to them.
|
||||||
# You must have an amateur radio callsign to use this software. You must
|
# You must have an amateur radio callsign to use this software. You must
|
||||||
@ -22,23 +21,22 @@
|
|||||||
|
|
||||||
# python included libs
|
# python included libs
|
||||||
import logging
|
import logging
|
||||||
|
from logging import NullHandler
|
||||||
|
from logging.handlers import RotatingFileHandler
|
||||||
import os
|
import os
|
||||||
import queue
|
import queue
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from logging import NullHandler
|
|
||||||
from logging.handlers import RotatingFileHandler
|
|
||||||
|
|
||||||
import aprslib
|
|
||||||
import click
|
|
||||||
import click_completion
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
# local imports here
|
# local imports here
|
||||||
import aprsd
|
import aprsd
|
||||||
from aprsd import client, email, messaging, plugin, threads, utils
|
from aprsd import client, email, messaging, plugin, threads, utils
|
||||||
|
import aprslib
|
||||||
|
import click
|
||||||
|
import click_completion
|
||||||
|
import yaml
|
||||||
|
|
||||||
# setup the global logger
|
# setup the global logger
|
||||||
# logging.basicConfig(level=logging.DEBUG) # level=10
|
# logging.basicConfig(level=logging.DEBUG) # level=10
|
||||||
@ -99,7 +97,9 @@ def main():
|
|||||||
|
|
||||||
@main.command()
|
@main.command()
|
||||||
@click.option(
|
@click.option(
|
||||||
"-i", "--case-insensitive/--no-case-insensitive", help="Case insensitive completion"
|
"-i",
|
||||||
|
"--case-insensitive/--no-case-insensitive",
|
||||||
|
help="Case insensitive completion",
|
||||||
)
|
)
|
||||||
@click.argument(
|
@click.argument(
|
||||||
"shell",
|
"shell",
|
||||||
@ -118,10 +118,14 @@ def show(shell, case_insensitive):
|
|||||||
|
|
||||||
@main.command()
|
@main.command()
|
||||||
@click.option(
|
@click.option(
|
||||||
"--append/--overwrite", help="Append the completion code to the file", default=None
|
"--append/--overwrite",
|
||||||
|
help="Append the completion code to the file",
|
||||||
|
default=None,
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"-i", "--case-insensitive/--no-case-insensitive", help="Case insensitive completion"
|
"-i",
|
||||||
|
"--case-insensitive/--no-case-insensitive",
|
||||||
|
help="Case insensitive completion",
|
||||||
)
|
)
|
||||||
@click.argument(
|
@click.argument(
|
||||||
"shell",
|
"shell",
|
||||||
@ -137,16 +141,19 @@ def install(append, case_insensitive, shell, path):
|
|||||||
else {}
|
else {}
|
||||||
)
|
)
|
||||||
shell, path = click_completion.core.install(
|
shell, path = click_completion.core.install(
|
||||||
shell=shell, path=path, append=append, extra_env=extra_env
|
shell=shell,
|
||||||
|
path=path,
|
||||||
|
append=append,
|
||||||
|
extra_env=extra_env,
|
||||||
)
|
)
|
||||||
click.echo("%s completion installed in %s" % (shell, path))
|
click.echo("{} completion installed in {}".format(shell, path))
|
||||||
|
|
||||||
|
|
||||||
def signal_handler(signal, frame):
|
def signal_handler(signal, frame):
|
||||||
global server_vent
|
global server_vent
|
||||||
|
|
||||||
LOG.info(
|
LOG.info(
|
||||||
"Ctrl+C, Sending all threads exit! Can take up to 10 seconds to exit all threads"
|
"Ctrl+C, Sending all threads exit! Can take up to 10 seconds to exit all threads",
|
||||||
)
|
)
|
||||||
threads.APRSDThreadList().stop_all()
|
threads.APRSDThreadList().stop_all()
|
||||||
server_event.set()
|
server_event.set()
|
||||||
@ -191,7 +198,8 @@ def sample_config():
|
|||||||
default="DEBUG",
|
default="DEBUG",
|
||||||
show_default=True,
|
show_default=True,
|
||||||
type=click.Choice(
|
type=click.Choice(
|
||||||
["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"], case_sensitive=False
|
["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"],
|
||||||
|
case_sensitive=False,
|
||||||
),
|
),
|
||||||
show_choices=True,
|
show_choices=True,
|
||||||
help="The log level to use for aprsd.log",
|
help="The log level to use for aprsd.log",
|
||||||
@ -220,7 +228,13 @@ def sample_config():
|
|||||||
@click.argument("tocallsign")
|
@click.argument("tocallsign")
|
||||||
@click.argument("command", nargs=-1)
|
@click.argument("command", nargs=-1)
|
||||||
def send_message(
|
def send_message(
|
||||||
loglevel, quiet, config_file, aprs_login, aprs_password, tocallsign, command
|
loglevel,
|
||||||
|
quiet,
|
||||||
|
config_file,
|
||||||
|
aprs_login,
|
||||||
|
aprs_password,
|
||||||
|
tocallsign,
|
||||||
|
command,
|
||||||
):
|
):
|
||||||
"""Send a message to a callsign via APRS_IS."""
|
"""Send a message to a callsign via APRS_IS."""
|
||||||
global got_ack, got_response
|
global got_ack, got_response
|
||||||
@ -273,7 +287,9 @@ def send_message(
|
|||||||
got_response = True
|
got_response = True
|
||||||
# Send the ack back?
|
# Send the ack back?
|
||||||
ack = messaging.AckMessage(
|
ack = messaging.AckMessage(
|
||||||
config["aprs"]["login"], fromcall, msg_id=msg_number
|
config["aprs"]["login"],
|
||||||
|
fromcall,
|
||||||
|
msg_id=msg_number,
|
||||||
)
|
)
|
||||||
ack.send_direct()
|
ack.send_direct()
|
||||||
|
|
||||||
@ -312,7 +328,8 @@ def send_message(
|
|||||||
default="DEBUG",
|
default="DEBUG",
|
||||||
show_default=True,
|
show_default=True,
|
||||||
type=click.Choice(
|
type=click.Choice(
|
||||||
["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"], case_sensitive=False
|
["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"],
|
||||||
|
case_sensitive=False,
|
||||||
),
|
),
|
||||||
show_choices=True,
|
show_choices=True,
|
||||||
help="The log level to use for aprsd.log",
|
help="The log level to use for aprsd.log",
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import abc
|
import abc
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
|
from multiprocessing import RawValue
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import pickle
|
import pickle
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from multiprocessing import RawValue
|
|
||||||
|
|
||||||
from aprsd import client, threads, utils
|
from aprsd import client, threads, utils
|
||||||
|
|
||||||
@ -19,7 +18,7 @@ LOG = logging.getLogger("APRSD")
|
|||||||
NULL_MESSAGE = -1
|
NULL_MESSAGE = -1
|
||||||
|
|
||||||
|
|
||||||
class MsgTrack(object):
|
class MsgTrack:
|
||||||
"""Class to keep track of outstanding text messages.
|
"""Class to keep track of outstanding text messages.
|
||||||
|
|
||||||
This is a thread safe class that keeps track of active
|
This is a thread safe class that keeps track of active
|
||||||
@ -47,7 +46,7 @@ class MsgTrack(object):
|
|||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
if cls._instance is None:
|
if cls._instance is None:
|
||||||
cls._instance = super(MsgTrack, cls).__new__(cls)
|
cls._instance = super().__new__(cls)
|
||||||
cls._instance.track = {}
|
cls._instance.track = {}
|
||||||
cls._instance.lock = threading.Lock()
|
cls._instance.lock = threading.Lock()
|
||||||
return cls._instance
|
return cls._instance
|
||||||
@ -129,7 +128,7 @@ class MsgTrack(object):
|
|||||||
self.track = {}
|
self.track = {}
|
||||||
|
|
||||||
|
|
||||||
class MessageCounter(object):
|
class MessageCounter:
|
||||||
"""
|
"""
|
||||||
Global message id counter class.
|
Global message id counter class.
|
||||||
|
|
||||||
@ -147,7 +146,7 @@ class MessageCounter(object):
|
|||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
"""Make this a singleton class."""
|
"""Make this a singleton class."""
|
||||||
if cls._instance is None:
|
if cls._instance is None:
|
||||||
cls._instance = super(MessageCounter, cls).__new__(cls)
|
cls._instance = super().__new__(cls)
|
||||||
cls._instance.val = RawValue("i", 1)
|
cls._instance.val = RawValue("i", 1)
|
||||||
cls._instance.lock = threading.Lock()
|
cls._instance.lock = threading.Lock()
|
||||||
return cls._instance
|
return cls._instance
|
||||||
@ -173,7 +172,7 @@ class MessageCounter(object):
|
|||||||
return str(self.val.value)
|
return str(self.val.value)
|
||||||
|
|
||||||
|
|
||||||
class Message(object, metaclass=abc.ABCMeta):
|
class Message(metaclass=abc.ABCMeta):
|
||||||
"""Base Message Class."""
|
"""Base Message Class."""
|
||||||
|
|
||||||
# The message id to send over the air
|
# The message id to send over the air
|
||||||
@ -204,7 +203,7 @@ class TextMessage(Message):
|
|||||||
message = None
|
message = None
|
||||||
|
|
||||||
def __init__(self, fromcall, tocall, message, msg_id=None, allow_delay=True):
|
def __init__(self, fromcall, tocall, message, msg_id=None, allow_delay=True):
|
||||||
super(TextMessage, self).__init__(fromcall, tocall, msg_id)
|
super().__init__(fromcall, tocall, msg_id)
|
||||||
self.message = message
|
self.message = message
|
||||||
# do we try and save this message for later if we don't get
|
# do we try and save this message for later if we don't get
|
||||||
# an ack? Some messages we don't want to do this ever.
|
# an ack? Some messages we don't want to do this ever.
|
||||||
@ -213,7 +212,10 @@ class TextMessage(Message):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""Build raw string to send over the air."""
|
"""Build raw string to send over the air."""
|
||||||
return "{}>APRS::{}:{}{{{}\n".format(
|
return "{}>APRS::{}:{}{{{}\n".format(
|
||||||
self.fromcall, self.tocall.ljust(9), self._filter_for_send(), str(self.id)
|
self.fromcall,
|
||||||
|
self.tocall.ljust(9),
|
||||||
|
self._filter_for_send(),
|
||||||
|
str(self.id),
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -222,7 +224,11 @@ class TextMessage(Message):
|
|||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
delta = now - self.last_send_time
|
delta = now - self.last_send_time
|
||||||
return "{}>{} Msg({})({}): '{}'".format(
|
return "{}>{} Msg({})({}): '{}'".format(
|
||||||
self.fromcall, self.tocall, self.id, delta, self.message
|
self.fromcall,
|
||||||
|
self.tocall,
|
||||||
|
self.id,
|
||||||
|
delta,
|
||||||
|
self.message,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _filter_for_send(self):
|
def _filter_for_send(self):
|
||||||
@ -259,9 +265,7 @@ class SendMessageThread(threads.APRSDThread):
|
|||||||
def __init__(self, message):
|
def __init__(self, message):
|
||||||
self.msg = message
|
self.msg = message
|
||||||
name = self.msg.message[:5]
|
name = self.msg.message[:5]
|
||||||
super(SendMessageThread, self).__init__(
|
super().__init__("SendMessage-{}-{}".format(self.msg.id, name))
|
||||||
"SendMessage-{}-{}".format(self.msg.id, name)
|
|
||||||
)
|
|
||||||
|
|
||||||
def loop(self):
|
def loop(self):
|
||||||
"""Loop until a message is acked or it gets delayed.
|
"""Loop until a message is acked or it gets delayed.
|
||||||
@ -326,11 +330,13 @@ class AckMessage(Message):
|
|||||||
"""Class for building Acks and sending them."""
|
"""Class for building Acks and sending them."""
|
||||||
|
|
||||||
def __init__(self, fromcall, tocall, msg_id):
|
def __init__(self, fromcall, tocall, msg_id):
|
||||||
super(AckMessage, self).__init__(fromcall, tocall, msg_id=msg_id)
|
super().__init__(fromcall, tocall, msg_id=msg_id)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "{}>APRS::{}:ack{}\n".format(
|
return "{}>APRS::{}:ack{}\n".format(
|
||||||
self.fromcall, self.tocall.ljust(9), self.id
|
self.fromcall,
|
||||||
|
self.tocall.ljust(9),
|
||||||
|
self.id,
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -378,7 +384,7 @@ class AckMessage(Message):
|
|||||||
class SendAckThread(threads.APRSDThread):
|
class SendAckThread(threads.APRSDThread):
|
||||||
def __init__(self, ack):
|
def __init__(self, ack):
|
||||||
self.ack = ack
|
self.ack = ack
|
||||||
super(SendAckThread, self).__init__("SendAck-{}".format(self.ack.id))
|
super().__init__("SendAck-{}".format(self.ack.id))
|
||||||
|
|
||||||
def loop(self):
|
def loop(self):
|
||||||
"""Separate thread to send acks with retries."""
|
"""Separate thread to send acks with retries."""
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# The base plugin class
|
# The base plugin class
|
||||||
import abc
|
import abc
|
||||||
import fnmatch
|
import fnmatch
|
||||||
@ -12,14 +11,12 @@ import shutil
|
|||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import pluggy
|
|
||||||
import requests
|
|
||||||
import six
|
|
||||||
from thesmuggler import smuggle
|
|
||||||
|
|
||||||
import aprsd
|
import aprsd
|
||||||
from aprsd import email, messaging
|
from aprsd import email, messaging
|
||||||
from aprsd.fuzzyclock import fuzzy
|
from aprsd.fuzzyclock import fuzzy
|
||||||
|
import pluggy
|
||||||
|
import requests
|
||||||
|
from thesmuggler import smuggle
|
||||||
|
|
||||||
# setup the global logger
|
# setup the global logger
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
@ -39,7 +36,7 @@ CORE_PLUGINS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PluginManager(object):
|
class PluginManager:
|
||||||
# The singleton instance object for this class
|
# The singleton instance object for this class
|
||||||
_instance = None
|
_instance = None
|
||||||
|
|
||||||
@ -52,7 +49,7 @@ class PluginManager(object):
|
|||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
"""This magic turns this into a singleton."""
|
"""This magic turns this into a singleton."""
|
||||||
if cls._instance is None:
|
if cls._instance is None:
|
||||||
cls._instance = super(PluginManager, cls).__new__(cls)
|
cls._instance = super().__new__(cls)
|
||||||
# Put any initialization here.
|
# Put any initialization here.
|
||||||
return cls._instance
|
return cls._instance
|
||||||
|
|
||||||
@ -79,7 +76,7 @@ class PluginManager(object):
|
|||||||
for mem_name, obj in inspect.getmembers(module):
|
for mem_name, obj in inspect.getmembers(module):
|
||||||
if inspect.isclass(obj) and self.is_plugin(obj):
|
if inspect.isclass(obj) and self.is_plugin(obj):
|
||||||
self.obj_list.append(
|
self.obj_list.append(
|
||||||
{"name": mem_name, "obj": obj(self.config)}
|
{"name": mem_name, "obj": obj(self.config)},
|
||||||
)
|
)
|
||||||
|
|
||||||
return self.obj_list
|
return self.obj_list
|
||||||
@ -108,14 +105,16 @@ class PluginManager(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
assert hasattr(module, class_name), "class {} is not in {}".format(
|
assert hasattr(module, class_name), "class {} is not in {}".format(
|
||||||
class_name, module_name
|
class_name,
|
||||||
|
module_name,
|
||||||
)
|
)
|
||||||
# click.echo('reading class {} from module {}'.format(
|
# click.echo('reading class {} from module {}'.format(
|
||||||
# class_name, module_name))
|
# class_name, module_name))
|
||||||
cls = getattr(module, class_name)
|
cls = getattr(module, class_name)
|
||||||
if super_cls is not None:
|
if super_cls is not None:
|
||||||
assert issubclass(cls, super_cls), "class {} should inherit from {}".format(
|
assert issubclass(cls, super_cls), "class {} should inherit from {}".format(
|
||||||
class_name, super_cls.__name__
|
class_name,
|
||||||
|
super_cls.__name__,
|
||||||
)
|
)
|
||||||
# click.echo('initialising {} with params {}'.format(class_name, kwargs))
|
# click.echo('initialising {} with params {}'.format(class_name, kwargs))
|
||||||
obj = cls(**kwargs)
|
obj = cls(**kwargs)
|
||||||
@ -131,13 +130,17 @@ class PluginManager(object):
|
|||||||
plugin_obj = None
|
plugin_obj = None
|
||||||
try:
|
try:
|
||||||
plugin_obj = self._create_class(
|
plugin_obj = self._create_class(
|
||||||
plugin_name, APRSDPluginBase, config=self.config
|
plugin_name,
|
||||||
|
APRSDPluginBase,
|
||||||
|
config=self.config,
|
||||||
)
|
)
|
||||||
if plugin_obj:
|
if plugin_obj:
|
||||||
LOG.info(
|
LOG.info(
|
||||||
"Registering Command plugin '{}'({}) '{}'".format(
|
"Registering Command plugin '{}'({}) '{}'".format(
|
||||||
plugin_name, plugin_obj.version, plugin_obj.command_regex
|
plugin_name,
|
||||||
)
|
plugin_obj.version,
|
||||||
|
plugin_obj.command_regex,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self._pluggy_pm.register(plugin_obj)
|
self._pluggy_pm.register(plugin_obj)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
@ -173,8 +176,10 @@ class PluginManager(object):
|
|||||||
if plugin_obj:
|
if plugin_obj:
|
||||||
LOG.info(
|
LOG.info(
|
||||||
"Registering Command plugin '{}'({}) '{}'".format(
|
"Registering Command plugin '{}'({}) '{}'".format(
|
||||||
o["name"], o["obj"].version, o["obj"].command_regex
|
o["name"],
|
||||||
)
|
o["obj"].version,
|
||||||
|
o["obj"].command_regex,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self._pluggy_pm.register(o["obj"])
|
self._pluggy_pm.register(o["obj"])
|
||||||
|
|
||||||
@ -203,8 +208,7 @@ class APRSDCommandSpec:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
class APRSDPluginBase(metaclass=abc.ABCMeta):
|
||||||
class APRSDPluginBase(object):
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
"""The aprsd config object is stored."""
|
"""The aprsd config object is stored."""
|
||||||
self.config = config
|
self.config = config
|
||||||
@ -257,7 +261,8 @@ class FortunePlugin(APRSDPluginBase):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
process = subprocess.Popen(
|
process = subprocess.Popen(
|
||||||
[fortune_path, "-s", "-n 60"], stdout=subprocess.PIPE
|
[fortune_path, "-s", "-n 60"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
)
|
)
|
||||||
reply = process.communicate()[0]
|
reply = process.communicate()[0]
|
||||||
reply = reply.decode(errors="ignore").rstrip()
|
reply = reply.decode(errors="ignore").rstrip()
|
||||||
@ -406,7 +411,10 @@ class TimePlugin(APRSDPluginBase):
|
|||||||
m = stm.tm_min
|
m = stm.tm_min
|
||||||
cur_time = fuzzy(h, m, 1)
|
cur_time = fuzzy(h, m, 1)
|
||||||
reply = "{} ({}:{} PDT) ({})".format(
|
reply = "{} ({}:{} PDT) ({})".format(
|
||||||
cur_time, str(h), str(m).rjust(2, "0"), message.rstrip()
|
cur_time,
|
||||||
|
str(h),
|
||||||
|
str(m).rjust(2, "0"),
|
||||||
|
message.rstrip(),
|
||||||
)
|
)
|
||||||
return reply
|
return reply
|
||||||
|
|
||||||
@ -497,7 +505,7 @@ class EmailPlugin(APRSDPluginBase):
|
|||||||
# send recipient link to aprs.fi map
|
# send recipient link to aprs.fi map
|
||||||
if content == "mapme":
|
if content == "mapme":
|
||||||
content = "Click for my location: http://aprs.fi/{}".format(
|
content = "Click for my location: http://aprs.fi/{}".format(
|
||||||
self.config["ham"]["callsign"]
|
self.config["ham"]["callsign"],
|
||||||
)
|
)
|
||||||
too_soon = 0
|
too_soon = 0
|
||||||
now = time.time()
|
now = time.time()
|
||||||
@ -521,7 +529,7 @@ class EmailPlugin(APRSDPluginBase):
|
|||||||
LOG.debug(
|
LOG.debug(
|
||||||
"DEBUG: email_sent_dict is big ("
|
"DEBUG: email_sent_dict is big ("
|
||||||
+ str(len(self.email_sent_dict))
|
+ str(len(self.email_sent_dict))
|
||||||
+ ") clearing out."
|
+ ") clearing out.",
|
||||||
)
|
)
|
||||||
self.email_sent_dict.clear()
|
self.email_sent_dict.clear()
|
||||||
self.email_sent_dict[ack] = now
|
self.email_sent_dict[ack] = now
|
||||||
@ -529,7 +537,7 @@ class EmailPlugin(APRSDPluginBase):
|
|||||||
LOG.info(
|
LOG.info(
|
||||||
"Email for message number "
|
"Email for message number "
|
||||||
+ ack
|
+ ack
|
||||||
+ " recently sent, not sending again."
|
+ " recently sent, not sending again.",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
reply = "Bad email address"
|
reply = "Bad email address"
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import abc
|
import abc
|
||||||
import logging
|
import logging
|
||||||
import queue
|
import queue
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import aprslib
|
|
||||||
|
|
||||||
from aprsd import client, messaging, plugin
|
from aprsd import client, messaging, plugin
|
||||||
|
import aprslib
|
||||||
|
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
|
||||||
@ -16,7 +14,7 @@ TX_THREAD = "TX"
|
|||||||
EMAIL_THREAD = "Email"
|
EMAIL_THREAD = "Email"
|
||||||
|
|
||||||
|
|
||||||
class APRSDThreadList(object):
|
class APRSDThreadList:
|
||||||
"""Singleton class that keeps track of application wide threads."""
|
"""Singleton class that keeps track of application wide threads."""
|
||||||
|
|
||||||
_instance = None
|
_instance = None
|
||||||
@ -26,7 +24,7 @@ class APRSDThreadList(object):
|
|||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
if cls._instance is None:
|
if cls._instance is None:
|
||||||
cls._instance = super(APRSDThreadList, cls).__new__(cls)
|
cls._instance = super().__new__(cls)
|
||||||
cls.lock = threading.Lock()
|
cls.lock = threading.Lock()
|
||||||
cls.threads_list = []
|
cls.threads_list = []
|
||||||
return cls._instance
|
return cls._instance
|
||||||
@ -48,7 +46,7 @@ class APRSDThreadList(object):
|
|||||||
|
|
||||||
class APRSDThread(threading.Thread, metaclass=abc.ABCMeta):
|
class APRSDThread(threading.Thread, metaclass=abc.ABCMeta):
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
super(APRSDThread, self).__init__(name=name)
|
super().__init__(name=name)
|
||||||
self.thread_stop = False
|
self.thread_stop = False
|
||||||
APRSDThreadList().add(self)
|
APRSDThreadList().add(self)
|
||||||
|
|
||||||
@ -67,7 +65,7 @@ class APRSDThread(threading.Thread, metaclass=abc.ABCMeta):
|
|||||||
|
|
||||||
class APRSDRXThread(APRSDThread):
|
class APRSDRXThread(APRSDThread):
|
||||||
def __init__(self, msg_queues, config):
|
def __init__(self, msg_queues, config):
|
||||||
super(APRSDRXThread, self).__init__("RX_MSG")
|
super().__init__("RX_MSG")
|
||||||
self.msg_queues = msg_queues
|
self.msg_queues = msg_queues
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
@ -112,7 +110,11 @@ class APRSDRXThread(APRSDThread):
|
|||||||
ack_num = packet.get("msgNo")
|
ack_num = packet.get("msgNo")
|
||||||
LOG.info("Got ack for message {}".format(ack_num))
|
LOG.info("Got ack for message {}".format(ack_num))
|
||||||
messaging.log_message(
|
messaging.log_message(
|
||||||
"ACK", packet["raw"], None, ack=ack_num, fromcall=packet["from"]
|
"ACK",
|
||||||
|
packet["raw"],
|
||||||
|
None,
|
||||||
|
ack=ack_num,
|
||||||
|
fromcall=packet["from"],
|
||||||
)
|
)
|
||||||
tracker = messaging.MsgTrack()
|
tracker = messaging.MsgTrack()
|
||||||
tracker.remove(ack_num)
|
tracker.remove(ack_num)
|
||||||
@ -153,7 +155,9 @@ class APRSDRXThread(APRSDThread):
|
|||||||
LOG.debug("Sending '{}'".format(reply))
|
LOG.debug("Sending '{}'".format(reply))
|
||||||
|
|
||||||
msg = messaging.TextMessage(
|
msg = messaging.TextMessage(
|
||||||
self.config["aprs"]["login"], fromcall, reply
|
self.config["aprs"]["login"],
|
||||||
|
fromcall,
|
||||||
|
reply,
|
||||||
)
|
)
|
||||||
self.msg_queues["tx"].put(msg)
|
self.msg_queues["tx"].put(msg)
|
||||||
else:
|
else:
|
||||||
@ -166,7 +170,9 @@ class APRSDRXThread(APRSDThread):
|
|||||||
|
|
||||||
reply = "Usage: {}".format(", ".join(names))
|
reply = "Usage: {}".format(", ".join(names))
|
||||||
msg = messaging.TextMessage(
|
msg = messaging.TextMessage(
|
||||||
self.config["aprs"]["login"], fromcall, reply
|
self.config["aprs"]["login"],
|
||||||
|
fromcall,
|
||||||
|
reply,
|
||||||
)
|
)
|
||||||
self.msg_queues["tx"].put(msg)
|
self.msg_queues["tx"].put(msg)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
@ -178,7 +184,9 @@ class APRSDRXThread(APRSDThread):
|
|||||||
# let any threads do their thing, then ack
|
# let any threads do their thing, then ack
|
||||||
# send an ack last
|
# send an ack last
|
||||||
ack = messaging.AckMessage(
|
ack = messaging.AckMessage(
|
||||||
self.config["aprs"]["login"], fromcall, msg_id=msg_id
|
self.config["aprs"]["login"],
|
||||||
|
fromcall,
|
||||||
|
msg_id=msg_id,
|
||||||
)
|
)
|
||||||
self.msg_queues["tx"].put(ack)
|
self.msg_queues["tx"].put(ack)
|
||||||
LOG.debug("Packet processing complete")
|
LOG.debug("Packet processing complete")
|
||||||
@ -213,7 +221,7 @@ class APRSDRXThread(APRSDThread):
|
|||||||
|
|
||||||
class APRSDTXThread(APRSDThread):
|
class APRSDTXThread(APRSDThread):
|
||||||
def __init__(self, msg_queues, config):
|
def __init__(self, msg_queues, config):
|
||||||
super(APRSDTXThread, self).__init__("TX_MSG")
|
super().__init__("TX_MSG")
|
||||||
self.msg_queues = msg_queues
|
self.msg_queues = msg_queues
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""Utilities and helper functions."""
|
"""Utilities and helper functions."""
|
||||||
|
|
||||||
import errno
|
import errno
|
||||||
import functools
|
import functools
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import click
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
from aprsd import plugin
|
from aprsd import plugin
|
||||||
|
import click
|
||||||
|
import yaml
|
||||||
|
|
||||||
# an example of what should be in the ~/.aprsd/config.yml
|
# an example of what should be in the ~/.aprsd/config.yml
|
||||||
DEFAULT_CONFIG_DICT = {
|
DEFAULT_CONFIG_DICT = {
|
||||||
@ -103,13 +101,13 @@ def get_config(config_file):
|
|||||||
"""This tries to read the yaml config from <config_file>."""
|
"""This tries to read the yaml config from <config_file>."""
|
||||||
config_file_expanded = os.path.expanduser(config_file)
|
config_file_expanded = os.path.expanduser(config_file)
|
||||||
if os.path.exists(config_file_expanded):
|
if os.path.exists(config_file_expanded):
|
||||||
with open(config_file_expanded, "r") as stream:
|
with open(config_file_expanded) as stream:
|
||||||
config = yaml.load(stream, Loader=yaml.FullLoader)
|
config = yaml.load(stream, Loader=yaml.FullLoader)
|
||||||
return config
|
return config
|
||||||
else:
|
else:
|
||||||
if config_file == DEFAULT_CONFIG_FILE:
|
if config_file == DEFAULT_CONFIG_FILE:
|
||||||
click.echo(
|
click.echo(
|
||||||
"{} is missing, creating config file".format(config_file_expanded)
|
"{} is missing, creating config file".format(config_file_expanded),
|
||||||
)
|
)
|
||||||
create_default_config()
|
create_default_config()
|
||||||
msg = (
|
msg = (
|
||||||
@ -144,7 +142,10 @@ def parse_config(config_file):
|
|||||||
if name and name not in config[section]:
|
if name and name not in config[section]:
|
||||||
if not default:
|
if not default:
|
||||||
fail(
|
fail(
|
||||||
"'%s' was not in '%s' section of config file" % (name, section)
|
"'{}' was not in '{}' section of config file".format(
|
||||||
|
name,
|
||||||
|
section,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
config[section][name] = default
|
config[section][name] = default
|
||||||
@ -166,7 +167,10 @@ def parse_config(config_file):
|
|||||||
# special check here to make sure user has edited the config file
|
# special check here to make sure user has edited the config file
|
||||||
# and changed the ham callsign
|
# and changed the ham callsign
|
||||||
check_option(
|
check_option(
|
||||||
config, "ham", "callsign", default_fail=DEFAULT_CONFIG_DICT["ham"]["callsign"]
|
config,
|
||||||
|
"ham",
|
||||||
|
"callsign",
|
||||||
|
default_fail=DEFAULT_CONFIG_DICT["ham"]["callsign"],
|
||||||
)
|
)
|
||||||
check_option(config, "aprs", "login")
|
check_option(config, "aprs", "login")
|
||||||
check_option(config, "aprs", "password")
|
check_option(config, "aprs", "password")
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from aprsd import plugin
|
from aprsd import plugin
|
||||||
|
36
pyproject.toml
Normal file
36
pyproject.toml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=46.0", "wheel"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[tool.black]
|
||||||
|
# Use the more relaxed max line length permitted in PEP8.
|
||||||
|
line-length = 88
|
||||||
|
target-version = ["py36", "py37", "py38"]
|
||||||
|
# black will automatically exclude all files listed in .gitignore
|
||||||
|
include = '\.pyi?$'
|
||||||
|
exclude = '''
|
||||||
|
/(
|
||||||
|
\.git
|
||||||
|
| \.hg
|
||||||
|
| \.mypy_cache
|
||||||
|
| \.tox
|
||||||
|
| \.venv
|
||||||
|
| _build
|
||||||
|
| buck-out
|
||||||
|
| build
|
||||||
|
| dist
|
||||||
|
)/
|
||||||
|
'''
|
||||||
|
|
||||||
|
[tool.isort]
|
||||||
|
profile = "black"
|
||||||
|
line_length = 88
|
||||||
|
force_sort_within_sections = true
|
||||||
|
# Inform isort of paths to import names that should be considered part of the "First Party" group.
|
||||||
|
src_paths = ["src/openstack_loadtest"]
|
||||||
|
skip_gitignore = true
|
||||||
|
# If you need to skip/exclude folders, consider using skip_glob as that will allow the
|
||||||
|
# isort defaults for skip to remain without the need to duplicate them.
|
||||||
|
|
||||||
|
[tool.coverage.run]
|
||||||
|
branch = true
|
11
setup.cfg
11
setup.cfg
@ -1,15 +1,16 @@
|
|||||||
[metadata]
|
[metadata]
|
||||||
name = aprsd
|
name = aprsd
|
||||||
summary = Amateur radio APRS daemon which listens for messages and responds
|
long_description = file: README.rst
|
||||||
description-file =
|
long_description_content_type = text/x-rst
|
||||||
README.rst
|
|
||||||
long-description-content-type = text/x-rst; charset=UTF-8
|
|
||||||
author = Craig Lamparter
|
author = Craig Lamparter
|
||||||
author-email = something@somewhere.com
|
author_email = something@somewhere.com
|
||||||
classifier =
|
classifier =
|
||||||
Topic :: Communications :: Ham Radio
|
Topic :: Communications :: Ham Radio
|
||||||
Operating System :: POSIX :: Linux
|
Operating System :: POSIX :: Linux
|
||||||
Programming Language :: Python
|
Programming Language :: Python
|
||||||
|
description_file =
|
||||||
|
README.rst
|
||||||
|
summary = Amateur radio APRS daemon which listens for messages and responds
|
||||||
|
|
||||||
[global]
|
[global]
|
||||||
setup-hooks =
|
setup-hooks =
|
||||||
|
1
setup.py
1
setup.py
@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
@ -7,7 +6,7 @@ from aprsd import email
|
|||||||
if sys.version_info >= (3, 2):
|
if sys.version_info >= (3, 2):
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
else:
|
else:
|
||||||
import mock
|
from unittest import mock
|
||||||
|
|
||||||
|
|
||||||
class TestMain(unittest.TestCase):
|
class TestMain(unittest.TestCase):
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import unittest
|
import unittest
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
@ -57,7 +56,10 @@ class TestPlugin(unittest.TestCase):
|
|||||||
|
|
||||||
message = "time"
|
message = "time"
|
||||||
expected = "{} ({}:{} PDT) ({})".format(
|
expected = "{} ({}:{} PDT) ({})".format(
|
||||||
cur_time, str(h), str(m).rjust(2, "0"), message.rstrip()
|
cur_time,
|
||||||
|
str(h),
|
||||||
|
str(m).rjust(2, "0"),
|
||||||
|
message.rstrip(),
|
||||||
)
|
)
|
||||||
actual = time_plugin.run(fromcall, message, ack)
|
actual = time_plugin.run(fromcall, message, ack)
|
||||||
self.assertEqual(expected, actual)
|
self.assertEqual(expected, actual)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user