From 8c00488583966318fb866e998fd46b6139c48707 Mon Sep 17 00:00:00 2001 From: James Brechtel Date: Sat, 9 May 2026 15:17:47 -0400 Subject: [PATCH] Basic config --- .gitignore | 24 +++++++++- config.sample.yaml | 12 +++++ .../__pycache__/__init__.cpython-312.pyc | Bin 141 -> 0 bytes src/honker/__pycache__/config.cpython-312.pyc | Bin 1018 -> 0 bytes src/honker/__pycache__/main.cpython-312.pyc | Bin 3339 -> 0 bytes .../__pycache__/processes.cpython-312.pyc | Bin 6031 -> 0 bytes src/honker/config.py | 45 ++++++++++++++++-- src/honker/main.py | 12 ++++- 8 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 config.sample.yaml delete mode 100644 src/honker/__pycache__/__init__.cpython-312.pyc delete mode 100644 src/honker/__pycache__/config.cpython-312.pyc delete mode 100644 src/honker/__pycache__/main.cpython-312.pyc delete mode 100644 src/honker/__pycache__/processes.cpython-312.pyc diff --git a/.gitignore b/.gitignore index 98d8a5a..6c3d6ce 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,23 @@ -logs +# Python +__pycache__/ +*.py[cod] +*.egg-info/ +*.egg +dist/ +build/ + +# Virtual environments +.venv/ + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Honker +logs/ diff --git a/config.sample.yaml b/config.sample.yaml new file mode 100644 index 0000000..7ea6603 --- /dev/null +++ b/config.sample.yaml @@ -0,0 +1,12 @@ +# Honker configuration +# Copy this file to ~/.config/honker/config.yaml and edit to suit your setup. + +# The Signal account number honker will use +account: "+15551234567" + +# Contacts that are allowed to interact with honker +trusted_contacts: + - name: "Alice" + number: "+15559876543" + - name: "Bob" + number: "+15551112222" diff --git a/src/honker/__pycache__/__init__.cpython-312.pyc b/src/honker/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 468013e64f538e8cbba583f2b5b596febfc724ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 141 zcmX@j%ge<81iUT(GePuY5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!($O!^FUr<0NG&SP z&r8hF&&bcqPA$?eE=q|kYwc^VF3c-(P2?_!+b$Tkq%WjBXo125ZqP2*$?_C4~w?@PXa`Mvkf?{5J_ zHXi-ben0?@sN;&KX`H+u#yU`d0(Fp~JEOxQlVPCqj?J(PP?=euoA_tnZkh^P6#-DV zhW1dM@%%T3Z$x{~W%?SKywa)&Dx<)vm(*-gy*W-1A9^yr3XJ-{(vGPpw7Ch%%vZf} z*(l|-8Jo`=rGkp8qT|OaQcQ%vq9*y>7(>|4>3yy%U>R8+7T1KVCYwXQutbZXQ} zl+ERhqKX7+hFmI>BSWd=Sax!}e{>-2z~K~4HXF0P1ScdIpbIp`cdKY9bGmvPbrF}I zJXVQ1W+8+hf$$l09s%(%*uAQ(Yirum!sdt-jP3ZWVDIZbD>%FhIzEJZ>gnfG8&l8j wZ}nQ?>pL@6xbKb63J>p&PFvyWT@d=l`{0#TrJi0Z90Ou*rN7e+b&^iM0lOIZ{{R30 diff --git a/src/honker/__pycache__/main.cpython-312.pyc b/src/honker/__pycache__/main.cpython-312.pyc deleted file mode 100644 index 611b8dace61a0f440f06168abace5796998a8e5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3339 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 b{rhbz2&%X1KjS{#LaXUFh98kGde#2|eq*av diff --git a/src/honker/config.py b/src/honker/config.py index b28c85a..4767b5a 100644 --- a/src/honker/config.py +++ b/src/honker/config.py @@ -1,4 +1,4 @@ -import os +from dataclasses import dataclass, field from pathlib import Path import yaml @@ -7,12 +7,49 @@ DEFAULT_CONFIG_PATH = Path.home() / ".config" / "honker" / "config.yaml" DEFAULT_SIGNAL_SOCKET_PATH = Path.home() / ".local" / "share" / "honker" / "signal-cli.sock" -def load_config(path: Path | None = None) -> dict: +@dataclass +class Contact: + name: str + number: str + + +@dataclass +class Config: + account: str + trusted_contacts: list[Contact] = field(default_factory=list) + + def is_trusted(self, number: str) -> bool: + """Check whether a phone number belongs to a trusted contact.""" + return any(c.number == number for c in self.trusted_contacts) + + def contact_by_number(self, number: str) -> Contact | None: + """Look up a trusted contact by phone number.""" + return next((c for c in self.trusted_contacts if c.number == number), None) + + +def load_config(path: Path | None = None) -> Config: """Load configuration from ~/.config/honker/config.yaml""" config_path = path or DEFAULT_CONFIG_PATH if not config_path.exists(): - return {} + raise FileNotFoundError( + f"Config file not found: {config_path}\n" + "Create it with at least:\n" + " account: +1234567890\n" + " trusted_contacts:\n" + ' - name: "Alice"\n' + " number: +1234567890" + ) with open(config_path) as f: - return yaml.safe_load(f) or {} + raw = yaml.safe_load(f) or {} + + if "account" not in raw: + raise ValueError("Config is missing required field: account") + + contacts = [ + Contact(name=c["name"], number=c["number"]) + for c in raw.get("trusted_contacts", []) + ] + + return Config(account=raw["account"], trusted_contacts=contacts) diff --git a/src/honker/main.py b/src/honker/main.py index 34237d3..0cd8109 100644 --- a/src/honker/main.py +++ b/src/honker/main.py @@ -13,6 +13,13 @@ log = logging.getLogger(__name__) def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description="Honker – Signal ↔ Goose ACP bridge") + parser.add_argument( + "--config", + metavar="FILE", + type=Path, + default=None, + help="Path to config file (default: ~/.config/honker/config.yaml)", + ) parser.add_argument( "--debug", metavar="DIR", @@ -31,8 +38,9 @@ def main() -> None: format="%(asctime)s [%(name)s] %(levelname)s: %(message)s", ) - config = load_config() - log.info("Honker starting up") + config = load_config(path=args.config) + log.info("Honker starting up (account=%s, %d trusted contacts)", + config.account, len(config.trusted_contacts)) if args.debug: log.debug("Debug mode enabled — process logs will be written to %s", args.debug)