Skip to content

Commit 16a4fa9

Browse files
Merge pull request #62 from priyanshujain/feat-apple-permission-probe
Add Apple permission probing during setup
2 parents 3265708 + 249bf9a commit 16a4fa9

File tree

5 files changed

+91
-3
lines changed

5 files changed

+91
-3
lines changed

internal/cli/setup.go

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/priyanshujain/openbotkit/oauth/google"
1717
"github.com/priyanshujain/openbotkit/remote"
1818
ansrc "github.com/priyanshujain/openbotkit/source/applenotes"
19+
contactsrc "github.com/priyanshujain/openbotkit/source/contacts"
1920
slacksrc "github.com/priyanshujain/openbotkit/source/slack"
2021
"github.com/priyanshujain/openbotkit/source/slack/desktop"
2122
"github.com/priyanshujain/openbotkit/store"
@@ -69,6 +70,7 @@ var setupCmd = &cobra.Command{
6970
sourceOptions = append(sourceOptions, huh.NewOption("Slack", "slack"))
7071
if runtime.GOOS == "darwin" {
7172
sourceOptions = append(sourceOptions, huh.NewOption("Apple Notes", "applenotes"))
73+
sourceOptions = append(sourceOptions, huh.NewOption("Apple Contacts", "applecontacts"))
7274
}
7375

7476
err = huh.NewForm(
@@ -123,6 +125,10 @@ var setupCmd = &cobra.Command{
123125
if err := setupAppleNotes(cfg); err != nil {
124126
return err
125127
}
128+
case "applecontacts":
129+
if err := setupAppleContacts(cfg); err != nil {
130+
return err
131+
}
126132
case "models":
127133
if err := setupModels(cfg); err != nil {
128134
return err
@@ -164,6 +170,8 @@ var setupCmd = &cobra.Command{
164170
fmt.Println(" - Run: obk auth whatsapp login")
165171
case "applenotes":
166172
fmt.Println(" - Apple Notes is ready (synced during setup)")
173+
case "applecontacts":
174+
fmt.Println(" - Apple Contacts is ready (synced during setup)")
167175
case "slack":
168176
fmt.Println(" - Slack is ready! Try: obk slack channels")
169177
}
@@ -395,13 +403,24 @@ func setupGWS(cfg *config.Config, services []string) error {
395403
}
396404

397405
func setupAppleNotes(cfg *config.Config) error {
406+
fmt.Println("\n Setting up Apple Notes...")
407+
fmt.Println(" macOS will ask for permission to access Notes.")
408+
fmt.Println(" Click \"OK\" to grant access.")
409+
fmt.Println()
410+
411+
if err := ansrc.CheckPermission(); err != nil {
412+
fmt.Println(" Permission denied or Notes not accessible.")
413+
fmt.Println(" Grant access in System Settings > Privacy & Security > Automation.")
414+
fmt.Println(" Then re-run: obk setup")
415+
return fmt.Errorf("apple notes permission: %w", err)
416+
}
417+
418+
fmt.Println(" Permission granted. Running initial sync...")
419+
398420
if err := config.EnsureSourceDir("applenotes"); err != nil {
399421
return fmt.Errorf("create applenotes dir: %w", err)
400422
}
401423

402-
fmt.Println("\n Setting up Apple Notes...")
403-
fmt.Println(" Running initial sync (this may take a few seconds)...")
404-
405424
db, err := store.Open(store.Config{
406425
Driver: cfg.AppleNotes.Storage.Driver,
407426
DSN: cfg.AppleNotesDataDSN(),
@@ -424,6 +443,49 @@ func setupAppleNotes(cfg *config.Config) error {
424443
return nil
425444
}
426445

446+
func setupAppleContacts(cfg *config.Config) error {
447+
fmt.Println("\n Setting up Apple Contacts...")
448+
fmt.Println(" macOS will ask for permission to access Contacts.")
449+
fmt.Println(" Click \"OK\" to grant access.")
450+
fmt.Println()
451+
452+
if err := contactsrc.CheckAppleContactsPermission(); err != nil {
453+
fmt.Println(" Permission denied or Contacts not accessible.")
454+
fmt.Println(" Grant access in System Settings > Privacy & Security > Automation.")
455+
fmt.Println(" Then re-run: obk setup")
456+
return fmt.Errorf("apple contacts permission: %w", err)
457+
}
458+
459+
fmt.Println(" Permission granted. Running initial sync...")
460+
461+
if err := config.EnsureSourceDir("contacts"); err != nil {
462+
return fmt.Errorf("create contacts dir: %w", err)
463+
}
464+
465+
db, err := store.Open(store.Config{
466+
Driver: cfg.Contacts.Storage.Driver,
467+
DSN: cfg.ContactsDataDSN(),
468+
})
469+
if err != nil {
470+
return fmt.Errorf("open database: %w", err)
471+
}
472+
defer db.Close()
473+
474+
result, err := contactsrc.Sync(db, nil, contactsrc.SyncOptions{
475+
Sources: []string{"applecontacts"},
476+
})
477+
if err != nil {
478+
return fmt.Errorf("apple contacts sync: %w", err)
479+
}
480+
481+
if err := config.LinkSource("contacts"); err != nil {
482+
return fmt.Errorf("link source: %w", err)
483+
}
484+
485+
fmt.Printf(" Synced %d contacts (%d new, %d linked)\n", result.Created+result.Linked, result.Created, result.Linked)
486+
return nil
487+
}
488+
427489
func setupSlack(cfg *config.Config) error {
428490
fmt.Println("\n -- Slack Setup --")
429491

source/applenotes/applescript.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,11 @@ func isRecentlyDeletedFolder(name string) bool {
183183
return recentlyDeletedNames[strings.ToLower(name)]
184184
}
185185

186+
func CheckPermission() error {
187+
_, err := runAppleScript(`tell application "Notes" to count of notes`)
188+
return err
189+
}
190+
186191
func parseAppleScriptDate(s string) time.Time {
187192
// AppleScript date format varies by locale. Common formats:
188193
formats := []string{

source/applenotes/sync_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ import (
99
"github.com/priyanshujain/openbotkit/store"
1010
)
1111

12+
func TestCheckPermission_Integration(t *testing.T) {
13+
if err := CheckPermission(); err != nil {
14+
t.Logf("permission check failed (may need to grant access): %v", err)
15+
}
16+
}
17+
1218
func TestFetchAllNotes_Integration(t *testing.T) {
1319
notes, err := FetchAllNotes()
1420
if err != nil {

source/contacts/sync_applecontacts.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,15 @@ end tell`, acFieldSep, acFieldSep, acFieldSep, acFieldSep, acRecordSep)
115115
return people, nil
116116
}
117117

118+
func CheckAppleContactsPermission() error {
119+
cmd := exec.Command("osascript", "-e", `tell application "Contacts" to count of people`)
120+
out, err := cmd.CombinedOutput()
121+
if err != nil {
122+
return fmt.Errorf("osascript: %s: %w", strings.TrimSpace(string(out)), err)
123+
}
124+
return nil
125+
}
126+
118127
func importApplePerson(db *store.DB, p applePerson, result *SyncResult) error {
119128
// Try to find existing contact by any phone or email.
120129
var contactID int64

source/contacts/sync_applecontacts_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ package contacts
44

55
import "testing"
66

7+
func TestCheckAppleContactsPermission_Integration(t *testing.T) {
8+
if err := CheckAppleContactsPermission(); err != nil {
9+
t.Logf("permission check failed (may need to grant access): %v", err)
10+
}
11+
}
12+
713
func TestFetchAppleContacts_Integration(t *testing.T) {
814
people, err := fetchAppleContacts()
915
if err != nil {

0 commit comments

Comments
 (0)