diff --git a/hermes_cli/config.py b/hermes_cli/config.py index 4357119a2..cf9889679 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -1125,6 +1125,27 @@ OPTIONAL_ENV_VARS = { "category": "messaging", "advanced": True, }, + "BLUEBUBBLES_SERVER_URL": { + "description": "BlueBubbles server URL for iMessage integration (e.g. http://192.168.1.10:1234)", + "prompt": "BlueBubbles server URL", + "url": "https://bluebubbles.app/", + "password": False, + "category": "messaging", + }, + "BLUEBUBBLES_PASSWORD": { + "description": "BlueBubbles server password (from BlueBubbles Server → Settings → API)", + "prompt": "BlueBubbles server password", + "url": None, + "password": True, + "category": "messaging", + }, + "BLUEBUBBLES_ALLOWED_USERS": { + "description": "Comma-separated iMessage addresses (email or phone) allowed to use the bot", + "prompt": "Allowed iMessage addresses (comma-separated)", + "url": None, + "password": False, + "category": "messaging", + }, "GATEWAY_ALLOW_ALL_USERS": { "description": "Allow all users to interact with messaging bots (true/false). Default: false.", "prompt": "Allow all users (true/false)", diff --git a/hermes_cli/setup.py b/hermes_cli/setup.py index 43c3b086d..95c9fa622 100644 --- a/hermes_cli/setup.py +++ b/hermes_cli/setup.py @@ -2167,6 +2167,71 @@ def _setup_whatsapp(): print_info("or personal self-chat) and pair via QR code.") +def _setup_bluebubbles(): + """Configure BlueBubbles iMessage gateway.""" + print_header("BlueBubbles (iMessage)") + existing = get_env_value("BLUEBUBBLES_SERVER_URL") + if existing: + print_info("BlueBubbles: already configured") + if not prompt_yes_no("Reconfigure BlueBubbles?", False): + return + + print_info("Connects Hermes to iMessage via BlueBubbles — a free, open-source") + print_info("macOS server that bridges iMessage to any device.") + print_info(" Requires a Mac running BlueBubbles Server v1.0.0+") + print_info(" Download: https://bluebubbles.app/") + print() + print_info("In BlueBubbles Server → Settings → API, note your Server URL and Password.") + print() + + server_url = prompt("BlueBubbles server URL (e.g. http://192.168.1.10:1234)") + if not server_url: + print_warning("Server URL is required — skipping BlueBubbles setup") + return + save_env_value("BLUEBUBBLES_SERVER_URL", server_url.rstrip("/")) + + password = prompt("BlueBubbles server password", password=True) + if not password: + print_warning("Password is required — skipping BlueBubbles setup") + return + save_env_value("BLUEBUBBLES_PASSWORD", password) + print_success("BlueBubbles credentials saved") + + print() + print_info("🔒 Security: Restrict who can message your bot") + print_info(" Use iMessage addresses: email (user@icloud.com) or phone (+15551234567)") + print() + allowed_users = prompt("Allowed iMessage addresses (comma-separated, leave empty for open access)") + if allowed_users: + save_env_value("BLUEBUBBLES_ALLOWED_USERS", allowed_users.replace(" ", "")) + print_success("BlueBubbles allowlist configured") + else: + print_info("⚠️ No allowlist set — anyone who can iMessage you can use the bot!") + + print() + print_info("📬 Home Channel: phone or email for cron job delivery and notifications.") + print_info(" You can also set this later with /set-home in your iMessage chat.") + home_channel = prompt("Home channel address (leave empty to set later)") + if home_channel: + save_env_value("BLUEBUBBLES_HOME_CHANNEL", home_channel) + + print() + print_info("Advanced settings (defaults are fine for most setups):") + if prompt_yes_no("Configure webhook listener settings?", False): + webhook_port = prompt("Webhook listener port (default: 8645)") + if webhook_port: + try: + save_env_value("BLUEBUBBLES_WEBHOOK_PORT", str(int(webhook_port))) + print_success(f"Webhook port set to {webhook_port}") + except ValueError: + print_warning("Invalid port number, using default 8645") + + print() + print_info("Requires the BlueBubbles Private API helper for typing indicators,") + print_info("read receipts, and tapback reactions. Basic messaging works without it.") + print_info(" Install: https://docs.bluebubbles.app/helper-bundle/installation") + + def _setup_webhooks(): """Configure webhook integration.""" print_header("Webhooks") @@ -2221,6 +2286,7 @@ _GATEWAY_PLATFORMS = [ ("Matrix", "MATRIX_ACCESS_TOKEN", _setup_matrix), ("Mattermost", "MATTERMOST_TOKEN", _setup_mattermost), ("WhatsApp", "WHATSAPP_ENABLED", _setup_whatsapp), + ("BlueBubbles (iMessage)", "BLUEBUBBLES_SERVER_URL", _setup_bluebubbles), ("Webhooks (GitHub, GitLab, etc.)", "WEBHOOK_ENABLED", _setup_webhooks), ] @@ -2264,6 +2330,7 @@ def setup_gateway(config: dict): or get_env_value("MATRIX_ACCESS_TOKEN") or get_env_value("MATRIX_PASSWORD") or get_env_value("WHATSAPP_ENABLED") + or get_env_value("BLUEBUBBLES_SERVER_URL") or get_env_value("WEBHOOK_ENABLED") ) if any_messaging: @@ -2283,6 +2350,8 @@ def setup_gateway(config: dict): missing_home.append("Discord") if get_env_value("SLACK_BOT_TOKEN") and not get_env_value("SLACK_HOME_CHANNEL"): missing_home.append("Slack") + if get_env_value("BLUEBUBBLES_SERVER_URL") and not get_env_value("BLUEBUBBLES_HOME_CHANNEL"): + missing_home.append("BlueBubbles") if missing_home: print() @@ -2453,6 +2522,8 @@ def _get_section_config_summary(config: dict, section_key: str) -> Optional[str] platforms.append("WhatsApp") if get_env_value("SIGNAL_ACCOUNT"): platforms.append("Signal") + if get_env_value("BLUEBUBBLES_SERVER_URL"): + platforms.append("BlueBubbles") if platforms: return ", ".join(platforms) return None # No platforms configured — section must run