-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathara_connector.py
More file actions
135 lines (117 loc) · 4.46 KB
/
Copy pathara_connector.py
File metadata and controls
135 lines (117 loc) · 4.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
"""Send/receive messages to the Ara agent via the Mac Messages app.
Ara phone number: +1 (415) 792-8699
Uses AppleScript to send via iMessage, and reads chat.db to poll for replies.
"""
import os
import sqlite3
import subprocess
import time
ARA_NUMBER = "+14157928699"
MESSAGES_DB = os.path.expanduser("~/Library/Messages/chat.db")
# Apple's epoch is 2001-01-01 UTC
APPLE_EPOCH_OFFSET = 978307200
def _escape_applescript(text: str) -> str:
return text.replace("\\", "\\\\").replace('"', '\\"')
def send_message(text: str) -> bool:
"""Send a message to Ara via iMessage. Returns True on success."""
safe = _escape_applescript(text)
script = f'''
tell application "Messages"
set targetService to 1st service whose service type = iMessage
set targetBuddy to buddy "{ARA_NUMBER}" of targetService
send "{safe}" to targetBuddy
end tell
'''
try:
result = subprocess.run(
["osascript", "-e", script],
capture_output=True, text=True, timeout=15,
)
if result.returncode != 0:
print(f"[ara_connector] AppleScript send error: {result.stderr.strip()}")
return False
return True
except Exception as e:
print(f"[ara_connector] send_message exception: {e}")
return False
def _apple_to_unix(apple_date: int) -> float:
"""Convert Apple Cocoa date (nanoseconds since 2001-01-01) to unix seconds."""
# Some older rows store seconds, newer rows store nanoseconds.
if apple_date > 1e12:
return (apple_date / 1e9) + APPLE_EPOCH_OFFSET
return apple_date + APPLE_EPOCH_OFFSET
def get_latest_reply(since_timestamp: float = None):
"""Return {'text', 'timestamp'} of the latest incoming msg from Ara, or None."""
if not os.path.exists(MESSAGES_DB):
return None
try:
conn = sqlite3.connect(f"file:{MESSAGES_DB}?mode=ro", uri=True, timeout=2)
cur = conn.cursor()
cur.execute(
"""
SELECT message.text, message.attributedBody, message.date, message.is_from_me
FROM message
JOIN handle ON message.handle_id = handle.ROWID
WHERE handle.id LIKE ?
AND message.is_from_me = 0
ORDER BY message.date DESC
LIMIT 5
""",
("%4157928699%",),
)
rows = cur.fetchall()
conn.close()
for text, attr_body, apple_date, _ in rows:
body = text
if not body and attr_body:
# Fallback: pull readable text from the typedstream blob.
try:
raw = bytes(attr_body)
idx = raw.find(b"NSString")
if idx != -1:
tail = raw[idx + 12 :]
# crude: take printable ASCII run
out = []
for b in tail:
if 32 <= b < 127 or b in (10, 13):
out.append(chr(b))
else:
if out:
break
body = "".join(out).strip() or None
except Exception:
body = None
if not body:
continue
unix_ts = _apple_to_unix(apple_date)
if since_timestamp is None or unix_ts > since_timestamp:
return {"text": body, "timestamp": unix_ts}
return None
except sqlite3.OperationalError as e:
print(f"[ara_connector] DB access error (grant Full Disk Access to Terminal): {e}")
return None
except Exception as e:
print(f"[ara_connector] get_latest_reply error: {e}")
return None
def ask_ara(question: str, timeout: int = 30) -> str | None:
"""Send a '?' prefixed question and poll for Ara's reply."""
q = question.strip()
if not q.startswith("?"):
q = "? " + q
before = get_latest_reply()
before_ts = before["timestamp"] if before else (time.time() - 1)
if not send_message(q):
return None
start = time.time()
while time.time() - start < timeout:
reply = get_latest_reply(since_timestamp=before_ts)
if reply:
return reply["text"]
time.sleep(1.5)
return None
if __name__ == "__main__":
import sys
q = " ".join(sys.argv[1:]) or "? test from ara copilot"
print(f"Asking Ara: {q}")
reply = ask_ara(q, timeout=40)
print(f"Reply: {reply}")