main
John Lauer Grok Bot.exe host plus app-control verbs and grok_webhook_ensure so the runtime scrapes the webhook pane itself. b473054 1mo ago
"""grok_send — toast + durable inbox. Desktop typing is opt-in (don't steal keyboard)."""

from __future__ import annotations

import json
import os
from datetime import datetime, timezone
from pathlib import Path

import caller_identity

_LAST_SEND: dict | None = None


def inbox_path() -> Path:
    return Path.home() / ".adom" / "grok-inbox.jsonl"


def last_send() -> dict | None:
    return _LAST_SEND


def _append_inbox(record: dict) -> str | None:
    path = inbox_path()
    try:
        path.parent.mkdir(parents=True, exist_ok=True)
        with path.open("a", encoding="utf-8") as fh:
            fh.write(json.dumps(record, ensure_ascii=False) + "\n")
        return str(path)
    except OSError:
        return None


def handle_send(args: dict) -> dict:
    global _LAST_SEND
    message = args.get("message")
    if message is None:
        message = args.get("text") or args.get("body") or ""
    message = str(message)
    if not message.strip():
        return {
            "success": False,
            "errorCode": "message_required",
            "error": "grok_send needs {message}.",
            "_hint": "Call grok_send {\"message\":\"hello from Adom\"}. Optional agent names the Grok character.",
            "_next": "grok_send {\"message\":\"...\"}",
        }

    agent = (args.get("agent") or "Grok Bot").strip() or "Grok Bot"
    thread = caller_identity.thread_name() or ""
    record = {
        "ts": datetime.now(timezone.utc).isoformat(),
        "message": message,
        "agent": agent,
        "caller": thread,
        "pid": os.getpid(),
    }
    path = _append_inbox(record)

    toast = caller_identity.ad_callback(
        "notify_user",
        {
            "title": "Grok Bot",
            "body": f"Grok Bot: {message}",
            "level": "info",
        },
        on_behalf=True,
    )
    toast_ok = isinstance(toast, dict) and toast.get("success") is not False
    if isinstance(toast, dict) and (toast.get("error") or "").find("not in env") >= 0:
        toast_ok = False

    # Look for a chat window so we can HINT that typing is opt-in — never type by default.
    windows = caller_identity.ad_callback(
        "desktop_list_windows", {"titleContains": "Grok Bot"}, on_behalf=True,
    )
    found_chat = False
    if isinstance(windows, dict):
        for key in ("windows", "items", "result"):
            val = windows.get(key)
            if isinstance(val, list) and val:
                found_chat = True
                break

    _LAST_SEND = {
        "ts": record["ts"],
        "message": message,
        "agent": agent,
        "inboxPath": path,
        "queued": True,
        "via": "notify+inbox",
        "toastOk": toast_ok,
        "chatWindowSeen": found_chat,
    }

    hint_parts = [
        "Queued via notify_user toast + durable inbox "
        f"({path or '~/.adom/grok-inbox.jsonl'}).",
        "via=notify+inbox — this is NOT proof the Grok Bot chat widget received a keystroke.",
    ]
    if found_chat:
        hint_parts.append(
            "A Grok Bot chat window is open. Desktop typing is OPT-IN "
            "(don't steal the keyboard). Pass typeIntoChat:true only if the user asked "
            "you to type; default is toast + inbox the Anywhere character can poll."
        )
    else:
        hint_parts.append(
            "No Grok-titled window seen. The inbox still got the line; grok_open if you "
            "need the Grok Bot UI."
        )
    if args.get("typeIntoChat"):
        hint_parts.append(
            "typeIntoChat was set, but this bridge will not inject keystrokes by default "
            "(focus-steal). Ask the user, then use an explicit desktop_press_key path from "
            "the CLI — never from this HTTP callback's cliRequired-forbidden set."
        )

    return {
        "success": True,
        "queued": True,
        "via": "notify+inbox",
        "agent": agent,
        "inboxPath": path,
        "toastOk": toast_ok,
        "chatWindowSeen": found_chat,
        "record": record,
        "_hint": " ".join(hint_parts),
    }