Last active
May 27, 2024 22:10
-
-
Save sychou/b5f30b9c4c5dc9cbc6c55f6bb0a3b500 to your computer and use it in GitHub Desktop.
Export Obsidian People to Contacts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--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