+(defun gnorb-bracket-message-id (id)
+ "Ensure message-id ID is bound by angle brackets."
+ ;; Always use a message-id with angle brackets around it.
+ ;; `gnus-summary-goto-article' can handle either, but
+ ;; `gnus-request-head' will fail without brackets IF you're
+ ;; requesting from an nntp group. Mysterious.
+ (unless (string-match "\\`<" id)
+ (setq id (concat "<" id)))
+ (unless (string-match ">\\'" id)
+ (setq id (concat id ">")))
+ id)
+
+(defun gnorb-unbracket-message-id (id)
+ "Ensure message-id ID is NOT bound by angle brackets."
+ ;; This shit is annoying, but Org wants an id with no brackets, and
+ ;; Gnus is safest with an id that has brackets. So here we are.
+ (replace-regexp-in-string "\\(\\`<\\|>\\'\\)" "" id))
+
+(defun gnorb-reply-to-gnus-link (link)
+ "Start a reply to the linked message."
+ (let* ((link (org-link-unescape link))
+ (group (car (org-split-string link "#")))
+ (id (gnorb-bracket-message-id
+ (second (org-split-string link "#"))))
+ (backend
+ (car (gnus-find-method-for-group group))))
+ (gnorb-follow-gnus-link group id)
+ (call-interactively
+ (if (eq backend 'nntp)
+ 'gnus-summary-followup-with-original
+ 'gnus-summary-wide-reply-with-original))))
+
+(defun gnorb-follow-gnus-link (group id)
+ "Be a little clever about following gnus links.
+
+The goal here is reuse frames and windows as much as possible, so
+we're not opening multiple windows on the *Group* buffer, for
+instance, and messing up people's layouts. There also seems to be
+an issue when opening a link to a message whose *Summary* buffer
+is already visible: somehow, after following the link, point ends
+up on the message _after_ the one we want, and things go haywire.
+
+So we try to be a little clever. The logical progression here is
+this:
+
+1. If the link's target group is already open in a *Summary*
+buffer, just switch to that buffer (if it's visible in any frame
+then raise it and switch focus, otherwise pull it into the
+current window) and go to the message with
+`gnus-summary-goto-article'.
+
+2. If the Gnus *Group* buffer is visible in any window or frame,
+raise that frame/window and give it focus before following the
+link.
+
+3. Otherwise just follow the link as usual, in the current
+window."
+ (let* ((sum-buffer (gnus-summary-buffer-name group))
+ (target-buffer
+ (cond
+ ((gnus-buffer-exists-p sum-buffer)
+ sum-buffer)
+ ((gnus-buffer-exists-p gnus-group-buffer)
+ gnus-group-buffer)
+ (t nil)))
+ (target-window (when target-buffer
+ (get-buffer-window target-buffer t))))
+ (if target-window
+ ;; Our target buffer is displayed somewhere: just go there.
+ (progn
+ (select-frame-set-input-focus
+ (window-frame target-window))
+ (switch-to-buffer target-buffer))
+ ;; Our target buffer exists, but isn't displayed: pull it up.
+ (if target-buffer
+ (switch-to-buffer target-buffer)))
+ (message "Following link...")
+ (if (gnus-buffer-exists-p sum-buffer)
+ (gnus-summary-goto-article id nil t)
+ (gnorb-open-gnus-link group id))))
+
+(defun gnorb-open-gnus-link (group id)
+ "Gnorb version of `org-gnus-follow-link'."
+ ;; We've probably already bracketed the id, but just in case this is
+ ;; called from elsewhere...
+ (let* ((id (gnorb-bracket-message-id id))
+ (art-no (cdr (gnus-request-head id group)))
+ (arts (gnus-group-unread group))
+ success)
+ (gnus-activate-group group)
+ (setq success (gnus-group-read-group arts t group))
+ (if success
+ (gnus-summary-goto-article (or art-no id) nil t)
+ (signal 'error "Group could not be opened."))))
+