al-erc.el 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. ;;; al-erc.el --- Additional functionality for ERC
  2. ;; Copyright © 2013–2016, 2018 Alex Kost
  3. ;; This program is free software; you can redistribute it and/or modify
  4. ;; it under the terms of the GNU General Public License as published by
  5. ;; the Free Software Foundation, either version 3 of the License, or
  6. ;; (at your option) any later version.
  7. ;;
  8. ;; This program is distributed in the hope that it will be useful,
  9. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. ;; GNU General Public License for more details.
  12. ;;
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Code:
  16. (require 'erc)
  17. (require 'erc-log)
  18. (require 'erc-networks)
  19. (require 'erc-stamp)
  20. (require 'erc-track)
  21. ;;;###autoload
  22. (defun al/erc-number-of-users ()
  23. "Show a number of users on the current channel."
  24. (interactive)
  25. (let ((channel (erc-default-target)))
  26. (if (and channel (erc-channel-p channel))
  27. (message "The number of users on %s: %d"
  28. channel
  29. (hash-table-count erc-channel-users))
  30. (user-error "The current buffer is not a channel"))))
  31. (defun al/znc-running-p ()
  32. "Return non-nil if 'znc' daemon is running."
  33. (string-match-p "\\`[[:digit:]]+ znc"
  34. (shell-command-to-string "pgrep -l znc")))
  35. (defun al/erc-server-buffer-name ()
  36. "Return a name of buffer with default server."
  37. (concat (erc-compute-server) ":"
  38. (number-to-string (erc-compute-port))))
  39. (defun al/erc-server-buffer (&optional noerror)
  40. "Return the current ERC server buffer.
  41. If NOERROR is non-nil, return nil instead of raising an error if
  42. the server buffer does not exist."
  43. (or (erc-server-buffer)
  44. (get-buffer (al/erc-server-buffer-name))
  45. (unless noerror
  46. (error "No active ERC server buffer"))))
  47. (defun al/erc-server-buffer-rename ()
  48. "Rename current server buffer (make a general name)."
  49. ;; Sometimes we need to modify names like "irc.freenode.net:7000<2>".
  50. (interactive)
  51. (let ((old-name (buffer-name))
  52. (new-name (al/erc-server-buffer-name)))
  53. (when (string-match (concat (erc-compute-server) ":.*")
  54. old-name)
  55. (rename-buffer new-name)
  56. (message "Current buffer was renamed from '%s' to '%s'."
  57. old-name new-name))))
  58. (defun al/erc-switch-to-server-buffer ()
  59. "Switch to ERC buffer with server."
  60. (interactive)
  61. (switch-to-buffer (al/erc-server-buffer)))
  62. ;;;###autoload
  63. (defun al/erc-switch-buffer ()
  64. "Switch to ERC buffer, or start ERC if not already started."
  65. (interactive)
  66. (let ((bufs (mapcar #'buffer-name (erc-buffer-list))))
  67. (if bufs
  68. (switch-to-buffer (completing-read "ERC buffer: " bufs))
  69. (erc))))
  70. ;;;###autoload
  71. (defun al/erc-track-switch-buffer (arg)
  72. "Same as `erc-track-switch-buffer', but start ERC if not already started."
  73. (interactive "p")
  74. (let ((buf (al/erc-server-buffer t)))
  75. (if buf
  76. (erc-track-switch-buffer arg)
  77. (erc))))
  78. (defun al/erc-get-channel-buffer-list ()
  79. "Return a list of the ERC-channel-buffers."
  80. (erc-buffer-filter
  81. (lambda () (string-match "^#.*" (buffer-name (current-buffer))))))
  82. ;;;###autoload
  83. (defun al/erc-cycle ()
  84. "Switch to ERC channel buffer, or run `erc-select'.
  85. When called repeatedly, cycle through the buffers."
  86. (interactive)
  87. (let ((buffers (al/erc-get-channel-buffer-list)))
  88. (if buffers
  89. (progn (when (eq (current-buffer) (car buffers))
  90. (bury-buffer)
  91. (setq buffers (cdr buffers)))
  92. (and buffers
  93. (switch-to-buffer (car buffers))))
  94. (call-interactively 'erc-select))))
  95. (defvar al/erc-channel-list '("#emacs" "#erc" "#gnus")
  96. "A list of channels used in `al/erc-join-channel'.")
  97. (defun al/erc-join-channel (channel &optional key)
  98. "Join CHANNEL.
  99. Similar to `erc-join-channel', but use `al/erc-channel-list'."
  100. (interactive
  101. (list
  102. (let* ((cur-sexp (thing-at-point 'sexp))
  103. (chn (if (and cur-sexp
  104. (eq 0 (string-match-p "#" cur-sexp)))
  105. cur-sexp
  106. "#")))
  107. (completing-read "Join channel: " al/erc-channel-list nil nil chn))
  108. (when (or current-prefix-arg erc-prompt-for-channel-key)
  109. (read-from-minibuffer "Channel key (RET for none): " nil))))
  110. (with-current-buffer (al/erc-server-buffer)
  111. (erc-cmd-JOIN channel (when (>= (length key) 1) key))))
  112. (defun al/erc-quit-server (reason)
  113. "Disconnect from current server.
  114. Similar to `erc-quit-server', but without prompting for REASON."
  115. (interactive (list ""))
  116. (with-current-buffer (al/erc-server-buffer)
  117. (erc-cmd-QUIT reason)))
  118. (defun al/erc-ghost-maybe (server nick)
  119. "Send GHOST message to NickServ if NICK ends with `erc-nick-uniquifier'.
  120. The function is suitable for `erc-after-connect'."
  121. (when (string-match (format "\\(.*?\\)%s+$" erc-nick-uniquifier) nick)
  122. (let ((nick-orig (match-string 1 nick))
  123. (password erc-session-password))
  124. (erc-message "PRIVMSG" (format "NickServ GHOST %s %s"
  125. nick-orig password))
  126. (erc-cmd-NICK nick-orig)
  127. (erc-message "PRIVMSG" (format "NickServ IDENTIFY %s %s"
  128. nick-orig password)))))
  129. (defun al/erc-insert-timestamp (string)
  130. "Insert timestamps in the beginning of the line.
  131. This function is suitable for `erc-insert-timestamp-function'.
  132. It is a sort of combination of `erc-insert-timestamp-left' and
  133. `erc-insert-timestamp-left-and-right'. Usual
  134. timestamps (`erc-timestamp-format') are inserted in the beginning
  135. of each line and an additional
  136. timestamp (`erc-timestamp-format-left') is inserted only if it
  137. was changed since the last time (by default if the date was
  138. changed)."
  139. (goto-char (point-min))
  140. (erc-put-text-property 0 (length string) 'field 'erc-timestamp string)
  141. (insert string)
  142. (let ((stamp (erc-format-timestamp (current-time)
  143. erc-timestamp-format-left)))
  144. (unless (string-equal stamp erc-timestamp-last-inserted-left)
  145. (goto-char (point-min))
  146. (erc-put-text-property 0 (length stamp) 'field 'erc-timestamp stamp)
  147. (insert stamp)
  148. (setq erc-timestamp-last-inserted-left stamp))))
  149. ;;; Away
  150. (defvar al/erc-away-msg-list '("just away" "learning emacs" "sleeping")
  151. "A list of away messages for `al/erc-away'.")
  152. (defun al/erc-away (&optional reason)
  153. "Mark the user as being away.
  154. Interactively prompt for reason; with prefix mark as unaway.
  155. Reasons are taken from `al/erc-away-msg-list'."
  156. (interactive
  157. (list (if current-prefix-arg
  158. ""
  159. (completing-read "Reason for AWAY: "
  160. al/erc-away-msg-list))))
  161. (with-current-buffer (al/erc-server-buffer)
  162. (erc-cmd-AWAY (or reason ""))))
  163. (defun al/erc-away-time ()
  164. "Return non-nil if the current ERC process is set away.
  165. Similar to `erc-away-time', but no need to be in ERC buffer."
  166. (with-current-buffer (al/erc-server-buffer)
  167. (erc-away-time)))
  168. ;;; CTCP info
  169. (defun al/erc-ctcp-query-FINGER (proc nick login host to msg)
  170. "Respond to a CTCP FINGER query."
  171. (unless erc-disable-ctcp-replies
  172. (erc-send-ctcp-notice nick "FINGER Keep your FINGER out of me."))
  173. nil)
  174. (defun al/erc-ctcp-query-ECHO (proc nick login host to msg)
  175. "Respond to a CTCP ECHO query."
  176. (when (string-match "^ECHO\\s-+\\(.*\\)\\s-*$" msg)
  177. (let ((str (apply #'string
  178. (reverse (string-to-list (match-string 1 msg))))))
  179. (unless erc-disable-ctcp-replies
  180. (erc-send-ctcp-notice nick (format "ECHO Did you mean '%s'?" str)))))
  181. nil)
  182. (defun al/erc-ctcp-query-TIME (proc nick login host to msg)
  183. "Respond to a CTCP TIME query."
  184. (unless erc-disable-ctcp-replies
  185. (let* ((hour (nth 2 (decode-time (current-time))))
  186. (str (cond ((al/erc-away-time) "time to be away")
  187. ((>= hour 18) "almost night")
  188. ((>= hour 12) (format-time-string "%A"))
  189. ((>= hour 6) "always morning")
  190. (t "time to sleep"))))
  191. (erc-send-ctcp-notice nick (format "TIME %s." str))))
  192. nil)
  193. (defun al/erc-ctcp-query-VERSION (proc nick login host to msg)
  194. "Respond to a CTCP VERSION query."
  195. (unless erc-disable-ctcp-replies
  196. (erc-send-ctcp-notice
  197. nick
  198. (format "VERSION ERC (GNU Emacs %s)" emacs-version)))
  199. nil)
  200. ;;; Log
  201. (defun al/erc-view-log-file ()
  202. "Visit a log file for the current ERC buffer."
  203. (interactive)
  204. (view-file (erc-current-logfile)))
  205. (defun al/erc-log-file-name-network-channel (buffer target nick server port)
  206. "Return erc log-file name of network (or server) and channel names.
  207. The result file name is in the form \"network_channel.txt\".
  208. This function is suitable for `erc-generate-log-file-name-function'."
  209. (with-current-buffer buffer
  210. (let* ((target (erc-default-target)) ; nil for server buffer
  211. (file (concat (or (erc-network-name) server)
  212. (and target (concat "_" target))
  213. ".txt")))
  214. ;; We need a make-safe-file-name function.
  215. (convert-standard-filename file))))
  216. ;; If you want to exclude a particular channel "#foochannel" and
  217. ;; channels that have "beard" in their names, use the following:
  218. ;;
  219. ;; (setq al/erc-log-excluded-regexps '("\\`#foochannel" "beard"))
  220. ;; (setq erc-enable-logging 'al/erc-log-all-but-some-buffers)
  221. ;;
  222. ;; Note: channel buffers may have names like "#foobar<2>", so too strict
  223. ;; regexps like "\\`#foochannel\\'" may be not good.
  224. (defvar al/erc-log-excluded-regexps nil
  225. "List of regexps for erc buffer names that will not be logged.")
  226. (defun al/erc-log-all-but-some-buffers (buffer)
  227. "Return t if logging should be enabled for BUFFER.
  228. Use `al/erc-log-excluded-regexps' to check if BUFFER should be
  229. logged or not.
  230. The function is intended to be used for `erc-enable-logging'."
  231. (cl-notany (lambda (re)
  232. (string-match-p re (buffer-name buffer)))
  233. al/erc-log-excluded-regexps))
  234. (provide 'al-erc)
  235. ;;; al-erc.el ends here