I've been trying to find a solution for this for a few days, and I keep running into questions I can't seem to answer.

My eventual goal is to capture a daily note in org roam, either with a custom function, or with the default org-roam-dailies-capture-today-function. I would then like a link to this note to appear in a separate, unopened file.

I have managed to piece together a function that asks for two roam nodes and pastes a link to one into the other, without opening the file (I used org-roam-node-read to select nodes and save them to variables, then read ID, title, and file path from those variables, with append-to-file to paste the link). This function, hypothetically, does exactly what I want except that it prompts me for file names.

Ideally, I would be able to capture a daily note and have the link automatically appear in a predetermined file. Some issues I ran into while trying to implement this:

  • There doesn't appear to be an applicable hook for capturing a daily note. I found org-roam-dailies-find-file-hook, which seems like it should work based on the help information - but when I tested it with an arbitrary function it just didn't do anything? (I used "(message "hello")", for what its worth).
  • I couldn't find a way to get the node information for the current buffer. org-roam-node-read was literally all I could find, which returns the roam node variable exactly as I want, but it prompts the user for a file - I want to be able to run the command based on current buffer (or some other way to specify today's daily note).
  • I couldn't find a way to programmatically execute the node-read command (I tried C-x M-: and it gave me "(eval-expression '(org-roam-node-read) nil nil 127)", which neither worked, nor changed when I chose other files.

I'm very confused. I'm sure I'm missing something obvious since it makes no sense for there to just not be a function to get the current node info, but I can't find anything even remotely useful online.

Edit: I managed to find org-roam-node-read--completions, which gives an alist made up of (what appears to be) pairs of hash tables for every node in the database. My latest thought is that I use elisp to make a string with the current date, then could search that list for a node name that matches that string. I've never worked with hash tables before though, and I'm not quite sure how i could even search the alist, since both elements in the alist pair appear to be hash tables (the first starts with "#(", which i'm not sure of, and the second starts with "#s(" which is definitely a hash table)

1 Answer 1


I managed to find a solution. I completely overlooked a function that solved my issue, was able to write a few new functions to implement what I was looking for.

If anyone's interested, I've included the relevant functions below.

  (defun cj/get-org-roam-node (note-title) ;; Intermediate function 
    "Get the Org-Roam node variable for a daily note with the specified title."
    (let* ((node (org-roam-node-from-title-or-alias note-title)))
      (if node
        (message "Note with title \"%s\" not found." note-title))))

  (defun cj/get-current-org-roam-daily-note-node () ;; Intermediate function
    "Get the Org-Roam node variable for the current daily note."
    (let* ((current-date (format-time-string "%Y-%m-%d"))
           (daily-note-title current-date)
           (node (cj/get-org-roam-node daily-note-title)))
      (if node
          (progn (setq cj/daily-note-node node) (message "%s" cj/daily-note-node))
        (message "Current daily note not found."))))

    (defun cj/roam-update-daily-moc ()
      "Runs automatically on roam capture today. Automatically adds an org id link to the roam node titled 'Daily MOC' if and only if the function has not run already today"

      (setq cj/roam-update-daily-moc-check (format-time-string "%Y-%m-%d")) ;; Sets the check variable to todays date everytime I try to update.

      (if (string= cj/roam-update-daily-moc-check cj/roam-update-daily-moc-check-end) ;; Checks whether the initial date (should be today) matches the last time the code ran (which is set later in the code)
          (message "MOC update has already been run today - aborting")
          (setq cj/daily-moc-node (cj/get-org-roam-node "Daily MOC"))
          (if (boundp 'cj/daily-moc-node)
                ;; DO something
                (message "Found Daily MOC"))
            (message "Daily MOC not found"))
          (setq link-id (org-roam-node-id cj/daily-note-node))
          (message "Link ID: %s" link-id) ;; These messages are here for debugging when this breaks.
          (setq link-title (org-roam-node-file-title cj/daily-note-node))
          (message "Link Title: %s" link-title)
          (setq destination-file (org-roam-node-file cj/daily-moc-node))
          (message "Destination File: %s" destination-file)
          (setq link-string (concat "[[id:" link-id "][" link-title "]]" "\n"))
          (message "Link String: %s" link-string)
          (append-to-file link-string nil destination-file)

          (setq cj/daily-moc-node nil)
          (setq link-id nil)
          (setq link-title nil)
          (setq destination-file nil)
          (setq link-string nil)
          (setq cj/daily-note-node nil)
          (setq cj/roam-update-daily-moc-check-end (format-time-string "%Y-%m-%d")) ; yes, I realize that using lets would be more concise and probably less resource intensive than setting global variables every time. I'm gonna change it later, but for now I didn't feel like messing with scopes and making sure everything returned correctly.
          (message "MOC update has not been run today - running")
          ) ;; End of the progn that runs the actual function

  (defun cj/capture-daily-node (&optional choice)
    (if (boundp 'choice)
        (progn (org-roam-dailies-capture-today nil choice) (cj/roam-update-daily-moc)
      (progn (setq choice (alt-completing-read "Capture: " '(("Default" . d) ("Entry" . e)) nil t "")) (org-roam-dailies-capture-today nil choice) (cj/roam-update-daily-moc))
      (setq choice nil)

      )) ;; I defined alt-completing-read elsewhere in my init file. It's pretty easy to find online though - I just copied the one I found

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.