I store my contacts in an org file called
people.org. The file has the following structure:
* John Doe :PROPERTIES: :ID: some-generated-uuid :GROUP: Work :PHONE: +1234567890 :ADDRESS_HOME: Foo bar street, no 5 :EMAIL: email@example.com :END: * Dohn Joe :PROPERTIES: :GROUP: High school :PHONE: +1334567890 :END: - Some notes about this person.
The nice part is, it's just plain org-mode. I only use top-level headings in this file, instead of creating header hierarchies. I utilize
:GROUP: property to categorize people, this way a person may belong to multiple categories. I use org-ql if I need to find people related to one group or if I want to filter them based on some specific property.
However the main use case is that I reference these headers in my other org files. For example, I also keep my diary in org-mode and I may write about some event that I participated with John Doe from above. I simply reference (using org links) to that person. The benefit of this is being able to recollect all of your notes about a particular person using one simple search.
Anyway, lets jump how I synchronize these contact information with my phone.
I simply create
.vcf file, a format that most of the contacts apps that are aware of, based on my
people.org file. Then I synchronize this
.vcf file to my phone, using Syncthing. The following snippet creates the
(defun isamert/build-contact-item (template-string contact-property) (if-let ((stuff (org-entry-get nil contact-property))) (concat (format template-string stuff) "\n") "")) (defun isamert/vcard () "Create a .vcf file containing all contact information." (interactive) (write-region (string-join (org-map-entries (lambda () (string-join `("BEGIN:VCARD\nVERSION:2.1\n" ,(format "UID:urn:uuid:%s\n" (org-id-get nil t)) ,(isamert/build-contact-item "FN:%s" "ITEM") ,(isamert/build-contact-item "TEL;CELL:%s" "PHONE") ,(isamert/build-contact-item "EMAIL:%s" "EMAIL") ,(isamert/build-contact-item "ORG:%s" "GROUP") ,(isamert/build-contact-item "ADR;HOME:;;%s" "ADDRESS_HOME") ,(isamert/build-contact-item "ADR;WORK:;;%s" "ADDRESS_WORK") ,(format "REV:%s\n" (format-time-string "%Y-%m-%dT%T")) "END:VCARD") "")) "LEVEL=1") "\n") nil (read-file-name "Where to save the .vcf file?" "~/Documents/sync/" "contacts.vcf")))
Simply call the
isamert/vcard function in your
people.org file and you get a
.vcf file. By default, it creates the file under
~/Documents/sync. This folder is automatically synced with my phone using Syncthing. Then I open my contacts app and import the file. That's it.
I used to earliest possible
.vcf format that is available so that every contacts app can import them. You can add/remove fields to your
.vcf export quite easily, just take a look at this wikipedia page for vCard and the relevant line to your function.
Here is an example, just to demonstrate how you obtain/copy email of one of your contacts interactively. A use case might be:
isamert/contacts-select-emailwhich presents you all of your contact's names.
To:field of your email client.
Don't forget to point
find-file-noselect to your
(defun isamert/contacts-email-alist () "Get an alist of contact name and emails." (seq-filter (lambda (it) it) (org-map-entries (lambda () (when-let ((email (org-entry-get nil "EMAIL"))) `(,(org-entry-get nil "ITEM") . ,email))) "LEVEL=1"))) (defun isamert/contacts-select-email () "Search through your contacts interactively and copy their email." (interactive) (with-current-buffer (find-file-noselect "~/Documents/notes/people.org") (let ((email-alist (isamert/contacts-email-alist))) (kill-new (cdr (assoc (completing-read "Copy email of: " email-alist) email-alist))))))
UIDsection to entries so that when you re-import your contacts after an update to the vcf file, already-existing contacts won't get duplicated.