From 07111be17f9af24a82617745bf3f72a9c98d5eac Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sat, 1 Nov 2025 22:37:38 -0500 Subject: feat: Add org-gcal dependencies and org-contacts birthday automation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit org-gcal-config.el: - Add use-package declarations for deferred and oauth2-auto - Ensures dependencies are automatically installed on fresh systems - Fixes "Cannot open load file" errors when syncing org-contacts-config.el: - Add automatic birthday timestamp insertion via capture template - Parse YYYY-MM-DD or MM-DD birthday formats - Insert yearly repeating timestamps after properties drawer - Add NICKNAME and NOTE fields to capture template org-agenda-config.el: - Enable chime-debug mode for troubleshooting 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- modules/org-contacts-config.el | 44 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) (limited to 'modules/org-contacts-config.el') diff --git a/modules/org-contacts-config.el b/modules/org-contacts-config.el index adb99db4..924b164c 100644 --- a/modules/org-contacts-config.el +++ b/modules/org-contacts-config.el @@ -37,6 +37,42 @@ ;; ----------------------- Org-Contacts Capture Template ----------------------- +(defun cj/org-contacts-finalize-birthday-timestamp () + "Add yearly repeating timestamp after properties drawer if BIRTHDAY is set." + (when (string= (plist-get org-capture-plist :key) "C") + (save-excursion + (goto-char (point-min)) + (let ((birthday (org-entry-get (point) "BIRTHDAY"))) + (when (and birthday (not (string-blank-p birthday))) + ;; Parse birthday - returns (year month day) or nil + (let ((parsed + (cond + ((string-match "^\\([0-9]\\{4\\}\\)-\\([0-9]\\{2\\}\\)-\\([0-9]\\{2\\}\\)$" birthday) + (list (string-to-number (match-string 1 birthday)) + (string-to-number (match-string 2 birthday)) + (string-to-number (match-string 3 birthday)))) + ((string-match "^\\([0-9]\\{2\\}\\)-\\([0-9]\\{2\\}\\)$" birthday) + (list (nth 5 (decode-time)) + (string-to-number (match-string 1 birthday)) + (string-to-number (match-string 2 birthday)))) + (t nil)))) + (when parsed + (let* ((year (nth 0 parsed)) + (month (nth 1 parsed)) + (day (nth 2 parsed)) + (time (encode-time 0 0 0 day month year)) + (dow (format-time-string "%a" time)) + (timestamp (format "<%04d-%02d-%02d %s +1y>" year month day dow)) + (heading-end (save-excursion (outline-next-heading) (point)))) + ;; Find :END: and insert timestamp + (when (re-search-forward "^[ \t]*:END:[ \t]*$" heading-end t) + (let ((end-pos (point))) + (goto-char end-pos) + (unless (re-search-forward "<[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}[^>]*\\+1y>" heading-end t) + (goto-char end-pos) + (end-of-line) + (insert "\n" timestamp)))))))))))) + (with-eval-after-load 'org-capture (add-to-list 'org-capture-templates '("C" "Contact" entry (file+headline contacts-file "Contacts") @@ -45,13 +81,15 @@ :EMAIL: %(cj/org-contacts-template-email) :PHONE: %^{Phone(s) - separate multiple with commas} :ADDRESS: %^{Address} -:BIRTHDAY: %^{Birthday (YYYY-MM-DD)} +:BIRTHDAY: %^{Birthday (YYYY-MM-DD or MM-DD)} +:NICKNAME: %^{Nickname} :COMPANY: %^{Company} :TITLE: %^{Title/Position} :WEBSITE: %^{URL} +:NOTE: %^{Notes} :END: -%^{Notes} -Added: %U"))) +Added: %U" + :prepare-finalize cj/org-contacts-finalize-birthday-timestamp))) ;; TASK: What purpose did this serve? ;; duplicate?!? -- cgit v1.2.3