;;; erc-init.el --- Various ERC hacks and configuration

;; This file is part of Michael Olson's Emacs settings.

;; The code in this file may be used, distributed, and modified
;; without restriction.

;; I use initsplit.el to separate customize settings on a per-project
;; basis.

;; Add working directories to load path
(add-to-list 'load-path "/home/mwolson/proj/emacs/erc/git-erc")

;; Load ERC
(require 'erc)

;; Load authentication info from an external source
(load "~/.emacs.d/.erc-auth")

;;; New ERC commands

(autoload 'doctor-doc "doctor")
(autoload 'make-doctor-variables "doctor")

(defvar erc-doctor-id "{Emacs doctor} ")

(defun erc-cmd-DOCTOR (&optional last-sender &rest ignore)
  "Get the last message in the channel and doctor it."
  (let ((limit (- (point) 1000))
        (pos (point))
        data
        doctor-buffer
        last-message
        text)
    ;; Make sure limit is not negative
    (when (< limit 0) (setq limit 0))
    ;; Search backwards for text from someone
    (while (and pos
                (not (and (setq data
                                (get-text-property pos 'erc-parsed))
                          (string= (aref data 3) "PRIVMSG")
                          (or (not last-sender)
                              (string= (car (split-string
                                             (aref data 2) "!"))
                                       last-sender)))))
      (setq pos (previous-single-property-change
                 pos 'erc-parsed nil limit))
      (when (= pos limit)
        (error "No appropriate previous message to doctor")))
    (when pos
      (setq last-sender (car (split-string
                              (aref (get-text-property
                                     pos 'erc-parsed) 2) "!"))
            doctor-buffer (concat "*ERC Doctor: " last-sender "*")
            last-message (split-string
                          ;; Remove punctuation from end of sentence
                          (replace-regexp-in-string
                           "[ .?!;,/]+$" ""
                           (aref (get-text-property pos
                                                    'erc-parsed) 5)))
            text (mapcar (lambda (s)
                           (intern (downcase s)))
                         ;; Remove salutation if it exists
                         (if (string-match
                              (concat "^" erc-valid-nick-regexp
                                      "[:,]*$\\|[:,]+$")
                              (car last-message))
                             (cdr last-message)
                           last-message))))
    (erc-send-message
     (concat erc-doctor-id
             ;; Only display sender if not in a query buffer
             (if (not (erc-query-buffer-p))
                 (concat last-sender ": "))
             (save-excursion
               (if (get-buffer doctor-buffer)
                   (set-buffer doctor-buffer)
                 (set-buffer (get-buffer-create doctor-buffer))
                 (make-doctor-variables))
               (erase-buffer)
               (doctor-doc text)
               (buffer-string))))))

(defun erc-cmd-EMMS (&rest ignore)
  "Display the current emms track to the current ERC buffer."
  (emms-player-mpd-show nil (lambda (buffer desc)
                              (with-current-buffer buffer
                                (erc-send-message desc)))))

(defalias 'erc-cmd-NP 'erc-cmd-EMMS)

(defun erc-cmd-UNAME (&rest ignore)
  "Display the result of running `uname -a' to the current ERC
buffer."
  (let ((uname-output
         (replace-regexp-in-string
          "[ \n]+$" "" (shell-command-to-string "uname -a"))))
    (erc-send-message
     (concat "{uname -a} [" uname-output "]"))))

(defun erc-cmd-UPTIME (&rest ignore)
  "Display the uptime of the system, as well as some load-related
stuff, to the current ERC buffer."
  (let ((uname-output
         (replace-regexp-in-string
          ", load average: " "] {Load average} ["
          ;; Collapse spaces, remove
          (replace-regexp-in-string
           " +" " "
           ;; Remove beginning and trailing whitespace
           (replace-regexp-in-string
            "^ +\\|[ \n]+$" ""
            (shell-command-to-string "uptime"))))))
    (erc-send-message
     (concat "{Uptime} [" uname-output "]"))))

(defun erc-cmd-WTF (term &rest ignore)
  "Look up definition for TERM."
  (let ((def (wtf-is term)))
    (if def
        (erc-send-message
         (concat "{Term} " (upcase term) " is " def))
      (message (concat "No definition found for " (upcase term))))))

(defun erc-cmd-XMMS (&rest ignore)
  "Display the current xmms track to the current ERC buffer."
  (let* ((xmms-output (shell-command-to-string
                       "xmms-shell -e current-track"))
         (case-fold-search t)
         (current-track
          (replace-regexp-in-string
           "[ \n]+$" "" (mapconcat
                         'identity
                         (nthcdr 3 (split-string xmms-output " "))
                         " "))))
    (if (null (string-match "current" xmms-output))
        (message xmms-output)
      (erc-send-message
       (concat "NP: [" current-track "]")))))

(defun erc-cmd-YOW (&rest ignore)
  "Display some pinhead wisdom into the current ERC buffer.  I'd
rather not see it messaged to me, just sent out."
  (let ((yow-msg (replace-regexp-in-string "\n" "" (yow nil nil))))
    (erc-send-message
     (concat "{Pinhead wisdom} "
             yow-msg))))

;;; Misc. hacks

;; Change fill column according to the width of the current frame
(defun my-erc-mode-stuff ()
  "Set fill column according to `frame-width'."
  (set (make-local-variable 'erc-fill-column) (- (frame-width) 10))
  (set (make-local-variable 'erc-timestamp-right-column)
       (1+ erc-fill-column)))
(add-hook 'erc-mode-hook 'my-erc-mode-stuff)

;;; Notify me when a keyword is matched (someone wants to reach me)

(defvar my-erc-page-message "%s is calling your name."
  "Format of message to display in dialog box")

(defvar my-erc-page-nick-alist nil
  "Alist of nicks and the last time they tried to trigger a
notification")

(defvar my-erc-page-timeout 30
  "Number of seconds that must elapse between notifications from
the same person.")

(defun my-erc-page-popup-notification (nick)
  (when window-system
    ;; must set default directory, otherwise start-process is unhappy
    ;; when this is something remote or nonexistent
    (let ((default-directory "~/"))
      ;; 8640000 milliseconds = 1 day
      (start-process "page-me" nil "notify-send"
                     "-u" "normal" "-t" "8640000" "ERC"
                     (format my-erc-page-message nick)))))

(defun my-erc-page-allowed (nick &optional delay)
  "Return non-nil if a notification should be made for NICK.
If DELAY is specified, it will be the minimum time in seconds
that can occur between two notifications.  The default is
`my-erc-page-timeout'."
  (unless delay (setq delay my-erc-page-timeout))
  (let ((cur-time (time-to-seconds (current-time)))
        (cur-assoc (assoc nick my-erc-page-nick-alist))
        (last-time nil))
    (if cur-assoc
        (progn
          (setq last-time (cdr cur-assoc))
          (setcdr cur-assoc cur-time)
          (> (abs (- cur-time last-time)) delay))
      (push (cons nick cur-time) my-erc-page-nick-alist)
      t)))

(defun my-erc-page-me (match-type nick message)
  "Notify the current user when someone sends a message that
matches a regexp in `erc-keywords'."
  (interactive)
  (when (and (eq match-type 'keyword)
             ;; I don't want to see anything from the erc server
             (not (string-match "\\`\\([sS]erver\\|localhost\\|root\\)" nick))
             ;; or bots
             (not (string-match "\\(^CIA[^!]*\\|bot\\|serv\\)!" nick))
             ;; or from those who abuse the system
             (my-erc-page-allowed nick))
    (my-erc-page-popup-notification nick)))
(add-hook 'erc-text-matched-hook 'my-erc-page-me)

(defun my-erc-page-me-PRIVMSG (proc parsed)
  (let ((nick (car (erc-parse-user (erc-response.sender parsed))))
        (target (car (erc-response.command-args parsed)))
        (msg (erc-response.contents parsed)))
    (when (and (erc-current-nick-p target)
               (not (erc-is-message-ctcp-and-not-action-p msg))
               (my-erc-page-allowed nick))
      (my-erc-page-popup-notification nick)
      nil)))
(add-hook 'erc-server-PRIVMSG-functions 'my-erc-page-me-PRIVMSG)

;;; Remove trailing whitespace in messages

(defun my-erc-remove-trailing-whitespace (proc parsed)
  "Remove trailing whitespace from the current message.
Some IM clients use an OTR plug-in that sends some annoying
trailing space to the screen, so we want to mop that up."
  (let ((msg (erc-response.contents parsed)))
    (when (stringp msg)
      (setf (erc-response.contents parsed)
            (erc-replace-regexp-in-string "[[:space:]]+\\'" "" msg))
      nil)))
(add-hook 'erc-server-PRIVMSG-functions 'my-erc-remove-trailing-whitespace)

;;; Key customizations

(global-set-key "\C-cea" (lambda () (interactive)
                           (erc :server "irc.arstechnica.com" :port "6667"
                                :nick "mwolson"
                                :full-name "http://mwolson.org")))
(global-set-key "\C-ceb" (lambda () (interactive)
                           (erc :server "localhost" :port "6668"
                                :nick "mwolson")))
(global-set-key "\C-cee" (lambda () (interactive)
                           (erc :server "irc.servercentral.net" :port "ircd"
                                :nick "mwolson")))
(global-set-key "\C-cef" (lambda () (interactive)
;                           (erc :server "irc.us.freenode.net" :port "ircd"
                           (erc :server "niven.freenode.net" :port "ircd"
                                :nick "mwolson")))
(global-set-key "\C-cer" (lambda () (interactive)
                           (erc-tls :server "irc.rizon.net" :port "6697"
                                    :nick "mwolson"
                                    :full-name "mwolson")))
(global-set-key "\C-ceo" (lambda () (interactive)
                           (erc :server "irc.oftc.net" :port "ircd"
                                :nick "bigmike160")))
(global-set-key "\C-cez" (lambda () (interactive)
                           (erc :server "irc.zirc.net" :port "ircd"
                                :nick "bigmike160")))

;; temporary
;; (global-set-key "\C-cet" #'(lambda nil
;;                              (interactive)
;;                              (message "%s" erc-autoaway-last-sent-time)))

;; Make C-c RET (or C-c C-RET) send messages instead of RET
(define-key erc-mode-map (kbd "RET") nil)
(define-key erc-mode-map (kbd "C-c RET") 'erc-send-current-line)
(define-key erc-mode-map (kbd "C-c C-RET") 'erc-send-current-line)

;; Disable some commands that I never want to execute
(define-key erc-mode-map "\C-c\C-c" nil)
(define-key erc-mode-map "\C-c\C-e" nil)
(define-key erc-mode-map "\C-c\C-f" nil)
(define-key erc-mode-map "\C-c\C-p" nil)
(define-key erc-mode-map "\C-c\C-q" nil)
(define-key erc-mode-map "\C-c\C-r" nil)

;;; Customization variables

(custom-set-variables
 '(erc-anonymous-login nil)
 '(erc-auto-query (quote bury))
 '(erc-autoaway-idle-seconds 1200)
 '(erc-autojoin-channels-alist (quote (("servercentral.net" "#gp2xdev") ("mwolson.org" "&bitlbee") ("freenode.net" "#erc" "#hcoop" "##metaconference" "#muse" "#tyrian"))))
 '(erc-bbdb-auto-create-on-whois-p t)
 '(erc-bbdb-popup-type nil)
 '(erc-beep-match-types nil)
 '(erc-current-nick-highlight-type nil)
 '(erc-dcc-get-default-directory "~/sandbox/")
 '(erc-dcc-listen-host nil)
 '(erc-dcc-port-range (quote (6888 . 6889)))
 '(erc-dcc-public-host nil)
 '(erc-email-userid "mwolson")
 '(erc-enable-logging (quote erc-log-all-but-server-buffers))
 '(erc-fool-highlight-type (quote all))
 '(erc-fools (quote ("AnalphaBestie" "ayrnieu" "DISABLEDconsolers" "rahul" "\\bams\\b" "dalias" "jerware" "jeramey")))
 '(erc-hide-prompt t)
 '(erc-hide-timestamps nil)
 '(erc-ignore-list (quote ("julian@pdpc/supporter/sustaining/ayrnieu")))
 '(erc-interpret-mirc-color t)
 '(erc-join-buffer (quote bury))
 '(erc-keywords (quote ("\\b\\(mike\\|mwolson\\)[!?.,;]*$" "\\(mike\\|mwolson\\)[:!?,;]+" "\\b\\([hH]ey\\|[hH]i\\) \\(mike\\|mwolson\\|man\\)\\b")))
 '(erc-kill-buffer-on-part t)
 '(erc-kill-queries-on-quit t)
 '(erc-kill-server-buffer-on-quit t)
 '(erc-log-write-after-insert t)
 '(erc-modules (quote (autoaway autojoin button capab-identify completion dcc fill identd irccontrols list log match menu move-to-prompt netsplit networks noncommands notify readonly ring scrolltobottom services stamp spelling track)))
 '(erc-nick "mwolson")
 '(erc-nicklist-icons-directory "/home/mwolson/proj/emacs/erc/arch-head/images/")
 '(erc-notify-list (quote ("docelic" "forcer" "Smerdyakov" "johnw" "sachac" "strfryed" "disumu" "megacz")))
 '(erc-notify-signoff-hook (quote (erc-notify-signoff)))
 '(erc-notify-signon-hook (quote (erc-notify-signon)))
 '(erc-pals (quote ("comcor" "deego" "docelic" "forcer" "johnsu01" "^johnw!" "^Luke!" "Sigma\\[Mtp\\]" "sachac" "strfryed")))
 '(erc-part-reason (quote erc-part-reason-various))
 '(erc-part-reason-various-alist (quote (("^np$" emms-show) ("zippy" erc-part-reason-zippy))))
 '(erc-prompt-for-nickserv-password nil)
 '(erc-prompt-for-password nil)
 '(erc-query-display (quote bury))
 '(erc-quit-reason (quote erc-quit-reason-various))
 '(erc-quit-reason-various-alist (quote (("^np$" emms-show) ("zippy" erc-part-reason-zippy))))
 '(erc-server "localhost")
 '(erc-server-send-ping-interval nil)
 '(erc-status-icon-file "/usr/local/share/emacs/23.0.50/etc/images/icons/hicolor/48x48/apps/emacs.png")
 '(erc-text-matched-hook (quote (my-erc-page-me erc-log-matches)))
 '(erc-timestamp-right-align-by-pixel t)
 '(erc-track-exclude-types (quote ("JOIN" "NICK" "PART" "QUIT" "333" "353")))
 '(erc-track-remove-disconnected-buffers t)
 '(erc-track-switch-direction (quote importance))
 '(erc-user-full-name (quote user-full-name))
 '(erc-verbose-dcc nil)
 '(erc-whowas-on-nosuchnick t))
(custom-set-faces
 '(erc-default-face ((t (:foreground "dark blue" :slant oblique))))
 '(erc-fool-face ((t (:foreground "LightBlue"))))
 '(erc-input-face ((t (:foreground "dim gray"))))
 '(erc-keyword-face ((t (:foreground "blue" :underline t :weight bold))))
 '(erc-timestamp-face ((t (:foreground "dark green" :weight bold)))))

;; Do only certain channels temporarily
;; (setq erc-autojoin-channels-alist
;;       '(("freenode.net" "#pulug" "#plugbot"))
;;       erc-nick "mwolson`")

;; (setq erc-modules (quote (autojoin button fill irccontrols log
;;                                    match netsplit noncommands
;;                                    pcomplete readonly ring stamp
;;                                    spelling track)))

;;; erc-init.el ends here