Skip to content

Instantly share code, notes, and snippets.

@sychou
Last active May 27, 2024 22:10
Show Gist options
  • Save sychou/b5f30b9c4c5dc9cbc6c55f6bb0a3b500 to your computer and use it in GitHub Desktop.
Save sychou/b5f30b9c4c5dc9cbc6c55f6bb0a3b500 to your computer and use it in GitHub Desktop.
Export Obsidian People to Contacts
--SCRIPT--
-- IMPORTANT SETTINGS
set peopleFolder to "/Users/sean/Vaults/Main/People/"
-- Create contacts from files
set fileNames to list folder (POSIX file peopleFolder) without invisibles
repeat with i from 1 to count of fileNames
set aFileName to item i of fileNames
set filePath to (peopleFolder & aFileName) as POSIX file
log filePath
-- Extract name from file name
set nameWithoutExtension to my getNameWithoutExtension(aFileName)
set contactDetails to my parseNameFromFileName(nameWithoutExtension)
set contactDetails to my parseContactDetails(filePath, contactDetails)
createContact(contactDetails)
end repeat
display dialog "Contacts have been updated successfully." buttons {"OK"} default button "OK"
-- Function to get name without extension
on getNameWithoutExtension(fileName)
set AppleScript's text item delimiters to "."
set nameParts to text items of fileName
set AppleScript's text item delimiters to ""
return item 1 of nameParts
end getNameWithoutExtension
-- Function to parse name from file name
on parseNameFromFileName(fileName)
set contactDetails to {firstName:"", lastName:"", companyName:"", phoneList:{}, emailList:{}, addressList:{}, birthday:"", notes:"", isCompany:false}
set nameParts to my split(fileName, " ")
if (count of nameParts) = 1 then
set contactDetails's firstName to item 1 of nameParts
else
set AppleScript's text item delimiters to " "
set contactDetails's firstName to (items 1 thru -2 of nameParts) as text
set AppleScript's text item delimiters to ""
set contactDetails's lastName to item -1 of nameParts
end if
return contactDetails
end parseNameFromFileName
-- Function to parse contact details from file
on parseContactDetails(filePath, contactDetails)
set fileContent to read file filePath as «class utf8»
-- Check for #company tag
if fileContent contains "#company" then
set contactDetails's isCompany to true
set contactDetails's companyName to contactDetails's firstName & " " & contactDetails's lastName
set contactDetails's firstName to ""
set contactDetails's lastName to ""
end if
-- Parse contact section
set contactStart to offset of "## Contact" in fileContent
set contactEnd to offset of "## Notes" in fileContent
if contactEnd is 0 then
set contactEnd to (length of fileContent) + 1
end if
set contactSection to text (contactStart + 10) thru (contactEnd - 1) of fileContent
-- Extract individual contact details
set contactLines to paragraphs of contactSection
repeat with contactLine in contactLines
if contactLine contains ":" then
set labelEnd to offset of ":" in contactLine
set fieldLabel to text 1 thru (labelEnd - 1) of contactLine
set fieldValue to text (labelEnd + 2) thru -1 of contactLine
if fieldLabel contains "Phone" then
set end of contactDetails's phoneList to {fieldLabel:fieldLabel, fieldValue:fieldValue}
else if fieldLabel contains "Email" then
set end of contactDetails's emailList to {fieldLabel:fieldLabel, fieldValue:fieldValue}
else if fieldLabel contains "Address" then
set end of contactDetails's addressList to {fieldLabel:fieldLabel, fieldValue:fieldValue}
else if fieldLabel is "Birthday" then
set contactDetails's birthday to fieldValue
end if
end if
end repeat
-- Parse notes section
set notesStart to offset of "## Notes" in fileContent
if notesStart is not 0 then
set contactDetails's notes to text (notesStart + 10) thru -1 of fileContent
end if
return contactDetails
end parseContactDetails
-- Helper function to split text
on split(s, delimiter)
set AppleScript's text item delimiters to delimiter
set theList to text items of s
set AppleScript's text item delimiters to ""
return theList
end split
-- Function to create a contact in the Contacts app
on createContact(contactDetails)
tell application "Contacts"
if contactDetails's isCompany then
set newContact to make new person with properties {company:true, organization:contactDetails's companyName}
else
set newContact to make new person with properties {first name:contactDetails's firstName, last name:contactDetails's lastName}
end if
repeat with phoneEntry in contactDetails's phoneList
make new phone at end of phones of newContact with properties {label:(phoneEntry's fieldLabel as text), value:(phoneEntry's fieldValue as text)}
end repeat
repeat with emailEntry in contactDetails's emailList
make new email at end of emails of newContact with properties {label:(emailEntry's fieldLabel as text), value:(emailEntry's fieldValue as text)}
end repeat
repeat with addressEntry in contactDetails's addressList
set addressString to addressEntry's fieldValue as text
set addressLabel to addressEntry's fieldLabel as text
-- Inline parseAddress function
set addressComponents to {street:"", city:"", state:"", zip:"", country:""}
set addressParts to my split(addressString, ", ")
if (count of addressParts) = 4 then
set addressComponents's street to item 1 of addressParts
set addressComponents's city to item 2 of addressParts
set cityStateZip to item 3 of addressParts
set cityStateZipParts to my split(cityStateZip, " ")
set addressComponents's state to item 1 of cityStateZipParts
set addressComponents's zip to item 2 of cityStateZipParts
set addressComponents's country to item 4 of addressParts
else if (count of addressParts) = 3 then
set addressComponents's street to item 1 of addressParts
set addressComponents's city to item 2 of addressParts
set cityStateZip to item 3 of addressParts
set cityStateZipParts to my split(cityStateZip, " ")
set addressComponents's state to item 1 of cityStateZipParts
if (count of cityStateZipParts) = 2 then
set addressComponents's zip to item 2 of cityStateZipParts
else
set addressComponents's zip to ""
end if
end if
make new address at end of addresses of newContact with properties {label:addressLabel, street:addressComponents's street as text, city:addressComponents's city as text, state:addressComponents's state as text, zip:addressComponents's zip as text, country:addressComponents's country as text}
end repeat
if contactDetails's birthday is not "" then
-- Assuming date format is "YYYY-MM-DD"
set dateParts to my split(contactDetails's birthday, "-")
set y to item 1 of dateParts as integer
set m to item 2 of dateParts as integer
set d to item 3 of dateParts as integer
-- Create date object
set dateObject to current date
set year of dateObject to y
set month of dateObject to m
set day of dateObject to d
if dateObject is not missing value then
set birth date of newContact to dateObject
end if
end if
if contactDetails's notes is not "" then
set note of newContact to contactDetails's notes
end if
save newContact
end tell
end createContact
--END SCRIPT--
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment