conf-erc.el 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. ;;; Code:
  2. (require 'znc)
  3. (require 'erc)
  4. (require 'erc-log)
  5. (require 'erc-join)
  6. (require 'erc-track)
  7. (require 'erc-input-fill)
  8. (require 'erc-services)
  9. (require 'alert)
  10. (defcustom distopico:erc-raw-connection nil
  11. "Erc connect without ZNC."
  12. :type 'boolean
  13. :group 'erc)
  14. (defcustom distopico:erc-servers-list '("irc.libera.chat:6697" "irc.indymedia.org:6697" "irc.w3.org")
  15. "Erc defaults servers to connect."
  16. :type 'list
  17. :group 'erc)
  18. (defcustom distopico:erc-alert-priority-target (concat "\\`[^&].*@libera\\'")
  19. "Priority regexp target/network for finger alert."
  20. :type 'regexp
  21. :group 'erc)
  22. (defcustom distopico:erc-alert-noise-regexp
  23. (concat "\\(Logging in:\\|Signing off\\|You're now away"
  24. "\\|Welcome back\\|Setting automatically away\\)")
  25. "This regexp matches unwanted noise."
  26. :type 'regexp
  27. :group 'erc)
  28. (defcustom distopico:erc-alert-priority-regexp
  29. (concat "^\\(<\\([^>]*\\)>\\)" "\\|^\\*\\*\\*\\s-\\w")
  30. "This regexp matches priority messages."
  31. :type 'regexp
  32. :group 'erc)
  33. (defcustom distopico:znc-server '("znc.distopico.info" 6697 t)
  34. "ZNC/irc defaults networks to connect."
  35. :type 'list
  36. :group 'erc)
  37. (defcustom distopico:znc-username "cerdolibre"
  38. "ZNC username to connect."
  39. :type 'string
  40. :group 'erc)
  41. (defcustom distopico:znc-networks '("libera" "indymedia") ;; TODO: fix duplicate name with w3#social
  42. "ZNC/irc defaults networks to connect."
  43. :type 'list
  44. :group 'erc)
  45. (defvar distopico:erc-reconnect-timer nil
  46. "Interval timer on reconnect.")
  47. (defvar distopico:erc-alert-last-message nil
  48. "Last alert message send.")
  49. ;; Basic
  50. (setq erc-header-line-uses-tabbar-p t
  51. erc-server-auto-reconnect t
  52. erc-server-reconnect-attempts t
  53. erc-track-exclude-server-buffer t
  54. erc-track-shorten-start 4
  55. erc-track-shorten-cutoff 5
  56. erc-track-shorten-aggressively t
  57. erc-minibuffer-ignored t ;; if we ignored something
  58. erc-track-showcount t
  59. ;; nil
  60. erc-prompt-for-nickserv-password nil
  61. ;; String
  62. erc-input-line-position -1
  63. erc-auto-query 'buffer
  64. erc-join-buffer 'bury
  65. erc-track-position-in-mode-line 'after-modes
  66. erc-current-nick-highlight-type 'nick-or-keyword
  67. erc-track-priority-faces-only 'all
  68. erc-echo-notice-hook '(erc-echo-notice-in-minibuffer)
  69. ;; Erc modules
  70. erc-modules '(pcomplete notifications spelling
  71. netsplit fill button match readonly
  72. track completion networks ring autojoin
  73. noncommands irccontrols move-to-prompt
  74. stamp menu list services truncate log)
  75. erc-track-faces-priority-list '(erc-error-face
  76. erc-current-nick-face
  77. erc-keyword-face
  78. erc-nick-msg-face
  79. erc-direct-msg-face
  80. erc-dangerous-host-face
  81. ;; erc-notice-face
  82. erc-prompt-face)
  83. ;; erc-user-full-name user-full-name
  84. erc-user-full-name "DistopicoVegan"
  85. erc-nick '("distopico" "DistopicoVegan")
  86. erc-keywords '("\\bdistopico\\b[^\]]")
  87. erc-mode-line-format "%a %t %o"
  88. ;; TODO: less silent tracking
  89. erc-hide-list '("JOIN" "PART" "QUIT")
  90. erc-track-exclude-types '("JOIN" "NICK" "PART" "QUIT" "MODE"
  91. "324" "329" "332" "333" "353" "477")
  92. erc-log-channels-directory (in-emacs-d ".cache/erc/logs/")
  93. erc-autojoin-channels-alist
  94. '((".*\\.libera.chat" "#emacs" "#gnu" "#emacs-es" "##vegan"
  95. "#social" "#libre.fm" "#parabola" "#trisquel-es" "#trisquel"
  96. "#mediagoblin" "#org-mode" "#pump.io" "#indieweb" "#social") ;
  97. (".*\\.w3.org", "#social")
  98. (".*\\.indymedia.org" "#riseup")))
  99. ;; Custom keys
  100. (define-key erc-mode-map (kbd "C-x c") 'distopico:erc-ido-switch-buffer)
  101. (define-key erc-mode-map (kbd "C-c C-r") 'distopico:erc-reset-track-mode)
  102. (define-key erc-mode-map (kbd "C-c M-a") 'erc-track-switch-buffer)
  103. (define-key erc-mode-map (kbd "C-q") 'distopico:erc-close)
  104. ;; Functions
  105. (defun distopico:erc-ignore-unimportant (msg)
  106. "Ignore some IRC `MSG', less noise in `erc-mode'."
  107. (if (or (string-match "*** localhost has changed mode for &bitlbee to" msg)
  108. (string-match "Account already online" msg)
  109. (string-match "Unknown error while loading configuration" msg))
  110. (setq erc-insert-this nil)))
  111. (defun distopico:erc-connect ()
  112. "Connect on all defaults servers."
  113. (interactive)
  114. (if distopico:erc-raw-connection
  115. ;; Connect only with ERC
  116. (dolist (server distopico:erc-servers-list)
  117. (let ((data (s-split "\\:" server))
  118. (buffer (get-buffer server)))
  119. (unless (erc-server-process-alive buffer)
  120. (let ((host (nth 0 data))
  121. (port (nth 1 data)))
  122. (when buffer
  123. (kill-buffer buffer))
  124. (if (equal port "6667")
  125. (erc :server host :port port :password nil)
  126. (erc-tls :server host :port port :password nil))))))
  127. ;; Connect to all networks with ZNC
  128. (distopico:erc-znc-setup)
  129. (condition-case nil
  130. (znc-all)
  131. (error (message "ZNC initialization failure")))))
  132. (defun distopico:erc-reconnect ()
  133. "Reconnect all active servers."
  134. (interactive)
  135. (dolist (server-buffer (erc-buffer-list #'erc-server-buffer-p))
  136. (when (and (not (erc-server-process-alive server-buffer))
  137. (erc-server-reconnect-p "close")
  138. (not erc-server-quitting))
  139. (distopico:erc-server-reconnect server-buffer))))
  140. (defun distopico:erc-close ()
  141. "Restore the previous window configuration and bury buffer."
  142. (interactive)
  143. (bury-buffer-restore-prev :erc-fullscreen))
  144. (defun distopico:erc-server-reconnect (buffer)
  145. "Check if `BUFFER' still live and try to reconnect."
  146. (when (buffer-live-p buffer)
  147. (with-current-buffer buffer
  148. (let ((active-buffers (distopico:erc-server-buffers-connect-p
  149. erc-session-server
  150. erc-session-port
  151. (erc-current-nick)))
  152. (server-buffers (distopico:erc-server-buffers-p
  153. erc-session-server
  154. erc-session-port
  155. (erc-current-nick))))
  156. ;; Veirfy if exist active server buffer
  157. (if active-buffers
  158. (unless (equal buffer (nth 0 active-buffers))
  159. (kill-buffer buffer))
  160. (progn
  161. (dolist (server-buffer server-buffers)
  162. ;; Kill similar server buffer, prevent duplicates
  163. (unless (equal buffer server-buffer)
  164. (kill-buffer buffer)))
  165. ;; Ensure if not has active connection yet
  166. (when (and (not erc-server-connected)
  167. (not erc-server-reconnecting))
  168. (erc-server-reconnect))))))))
  169. (defun distopico:erc-server-buffers-connect-p (server port nick)
  170. "Get buffers of `SERVER' connected with same `PORT' and `NICK' user."
  171. (erc-buffer-list
  172. (lambda ()
  173. (and (erc-server-buffer-p)
  174. (erc-server-process-alive)
  175. (string= erc-session-server server)
  176. (erc-port-equal erc-session-port port)
  177. (erc-current-nick-p nick)))))
  178. (defun distopico:erc-reset-track-mode ()
  179. "Clears out annoying `erc-track-mode' stuff for when we don't care."
  180. (interactive)
  181. (setq erc-modified-channels-alist nil)
  182. (erc-modified-channels-update))
  183. (defun distopico:erc-server-buffers-p (server port nick)
  184. "Get all buffers of `SERVER' with same `PORT' and `NICK' user."
  185. (erc-buffer-list
  186. (lambda ()
  187. (and (erc-server-buffer-p)
  188. (string= erc-session-server server)
  189. (erc-port-equal erc-session-port port)
  190. (erc-current-nick-p nick)))))
  191. (defun distopico:erc-start-ask-or-switch ()
  192. "Connect to ERC, or switch to last active buffer."
  193. (interactive)
  194. (if (> (length (distopico:erc-buffer-list)) 0)
  195. (progn
  196. (window-configuration-to-register :erc-fullscreen)
  197. (if erc-modified-channels-alist
  198. (erc-track-switch-buffer 1)
  199. (distopico:erc-ido-switch-buffer))
  200. (delete-other-windows))
  201. (when (y-or-n-p "Start ERC? ")
  202. (distopico:erc-connect))))
  203. (defun distopico:erc-ido-switch-buffer ()
  204. "Switch to ERC buffer using IDO.
  205. Choose which one or start ERC if not already started."
  206. (interactive)
  207. (ido-for-mode "ERC:" 'erc-mode))
  208. (defun distopico:erc-switch-to-irc ()
  209. "Switch to an IRC channel buffer, or run `erc-select'.
  210. When called repeatedly, cycle through the buffers `DEPRECATED'."
  211. (interactive)
  212. (let ((buffers (distopico:erc-global-get-channel-buffer-list)))
  213. (when (eq (current-buffer) (car buffers))
  214. (bury-buffer)
  215. (setq buffers (cdr buffers)))
  216. (if buffers
  217. (switch-to-buffer (car buffers))
  218. (call-interactively 'erc-select))))
  219. (defun distopico:erc-buffer-list ()
  220. "Return all ERC buffers by major mode."
  221. (let (final-list (list ))
  222. (dolist (buf (buffer-list) final-list)
  223. (if (equal 'erc-mode (with-current-buffer buf major-mode))
  224. (setq final-list (append (list (buffer-name buf)) final-list))))))
  225. (defun distopico:erc-global-get-channel-buffer-list ()
  226. "Return a list of the ERC-channel-buffers."
  227. (erc-buffer-filter '(lambda() (if (string-match "^[^#].*:\\([0-9]*\\|ircd\\)$" (buffer-name (current-buffer))) nil t)) nil))
  228. (defun distopico:get-erc-nickserv-passwords (host port user)
  229. "Read irc nickserv password by `HOST', `PORT' and `USER'."
  230. (let ((found
  231. (and (fboundp 'auth-source-search)
  232. (nth 0 (auth-source-search
  233. :user user
  234. :host host
  235. :port (if (numberp port) (number-to-string port) port)
  236. :max 1
  237. :require '(:secret))))))
  238. (if found
  239. (let ((secret (plist-get found :secret)))
  240. (copy-sequence
  241. (if (functionp secret)
  242. (funcall secret)
  243. secret))))))
  244. (defun distopico:erc-mode-line ()
  245. "Return user in current erc channel."
  246. (let ((ops 0)
  247. (voices 0)
  248. (members 0))
  249. (if erc-channel-users
  250. (maphash (lambda (key value)
  251. (when (erc-channel-user-op-p key)
  252. (setq ops (1+ ops)))
  253. (when (erc-channel-user-voice-p key)
  254. (setq voices (1+ voices)))
  255. (setq members (1+ members)))
  256. erc-channel-users))
  257. (format "• O: %S | V: %S | M: %S " ops voices members)))
  258. (defun distopico:erc-alert-important-p (info)
  259. "Return non-nil by `INFO' is a important notification to shown."
  260. (let ((erc-message (plist-get info :data)))
  261. (and erc-message
  262. (not (or (string-match "^\\** *Users on #" erc-message)
  263. (string-match distopico:erc-alert-noise-regexp
  264. erc-message))))))
  265. (defun distopico:erc-matched-or-insert-hook (&optional match-type nickuserhost message)
  266. "Send notification filter by custom rules see: `distopico:erc-define-alerts'.
  267. If has `MATCH-TYPE', `NICKUSERHOST' and `MESSAGE' from `erc-text-matched-hook'
  268. send notification when `distopico:erc-alert-important-p' is non-nil"
  269. (if (and (or (null match-type)
  270. (not (eq match-type 'fool)))
  271. (or (null distopico:erc-alert-last-message)
  272. (not (equal distopico:erc-alert-last-message message)))
  273. (string-match distopico:erc-alert-priority-regexp (or message "")))
  274. (let (alert-message)
  275. ;; Setup alert message
  276. (if (not message)
  277. (setq alert-message (buffer-string))
  278. (setq distopico:erc-alert-last-message message)
  279. (setq alert-message
  280. (concat "<"
  281. (nth 0 (erc-parse-user nickuserhost))
  282. "> " message)))
  283. ;; Send alert but the rules filter what will shown
  284. (alert alert-message
  285. :severity 'high
  286. :title (concat "ERC: " (buffer-name))
  287. :data message))))
  288. (defun distopico:erc-define-alerts ()
  289. "Define rules for `alert' mode for `erc-mode'."
  290. ;; Unless the user has recently typed in the ERC buffer, highlight the fringe
  291. (alert-add-rule
  292. :status '(buried visible idle)
  293. :severity '(moderate high urgent)
  294. :mode 'erc-mode
  295. :predicate
  296. #'(lambda (info)
  297. (and (not (eq (current-buffer) (plist-get info :buffer)))
  298. (or (string-match (concat "\\" (erc-current-nick) "[-a-z]*\\b")
  299. (plist-get info :message))
  300. (string-match distopico:erc-alert-priority-target
  301. (erc-format-target-and/or-network)))))
  302. :persistent
  303. #'(lambda (info)
  304. ;; If the buffer is buried, or the user has been idle for
  305. ;; `alert-reveal-idle-time' seconds, make this alert
  306. ;; persistent. Normally, alerts become persistent after
  307. ;; `alert-persist-idle-time' seconds.
  308. (memq (plist-get info :status) '(buried idle)))
  309. :style 'fringe
  310. :continue t)
  311. ;; Important notifications
  312. (alert-add-rule
  313. :status 'buried
  314. :mode 'erc-mode
  315. :predicate #'distopico:erc-alert-important-p
  316. :append t)
  317. ;; Ignore if not match with above rules
  318. (alert-add-rule :mode 'erc-mode :style 'ignore :append t))
  319. (defun distopico:erc-znc-setup ()
  320. "Setup configuration for ZNC server."
  321. (when distopico:znc-server
  322. (let ((networks-list (list ))
  323. (username distopico:znc-username)
  324. (server-conf distopico:znc-server)
  325. (host (nth 0 distopico:znc-server))
  326. (port (nth 1 distopico:znc-server))
  327. (pass))
  328. ;; Get password
  329. (setq pass (distopico:get-erc-nickserv-passwords host port username))
  330. ;; Networks lists
  331. (dolist (network distopico:znc-networks networks-list)
  332. (when (stringp network)
  333. (setq network (intern network)))
  334. (add-to-list 'networks-list (list network (format "%s/%s" username network) pass)))
  335. ;; Set configuration
  336. (setq znc-servers (list (append server-conf (list networks-list)))))))
  337. (defun distopico:erc-cancel-reconnect ()
  338. "Cancel re-connection attempt interval."
  339. (when distopico:erc-reconnect-timer
  340. (cancel-timer distopico:erc-reconnect-timer)
  341. (setq distopico:erc-reconnect-timer nil)))
  342. (defun distopico:erc-after-connect-hook (SERVER NICK)
  343. "When connect to irc `SERVER' send identify `NICK'."
  344. (let ((server (or erc-session-server SERVER))
  345. (port erc-session-port))
  346. (erc-nickserv-identify (distopico:get-erc-nickserv-passwords server port NICK))))
  347. (defun distopico:erc-before-connect-hook (SERVER PORT NICK)
  348. "Before connect to irc `SERVER' with `PORT' with identify `NICK'."
  349. (distopico:erc-cancel-reconnect))
  350. (defun distopico:erc-disconnected-hook (NICK HOSTNAME REASON)
  351. "Re-establish the connection by user `NICK'.
  352. even if the server closed it in `HOSTNAME' by `REASON'."
  353. (distopico:erc-cancel-reconnect)
  354. (setq distopico:erc-reconnect-timer
  355. (run-at-time nil (* 10 erc-server-reconnect-timeout) #'distopico:erc-reconnect)))
  356. (defun distopico:erc-mode-hook ()
  357. "Hook for `erc-mode' to setup mode settings."
  358. (visual-line-mode +1)
  359. (auto-fill-mode +1))
  360. (defun distopico:erc-init-load-hook ()
  361. "Hook when Emacs load."
  362. (distopico:erc-define-alerts)
  363. (run-at-time "10 sec" nil #'distopico:erc-connect))
  364. ;; Hooks
  365. ;;(add-hook 'erc-before-connect #'distopico:erc-before-connect-hook)
  366. ;;(add-hook 'erc-insert-pre-hook #'erc-ignore-unimportant)
  367. ;;(add-hook 'erc-disconnected-hook #'distopico:erc-disconnected-hook)
  368. (add-hook 'erc-mode-hook #'distopico:erc-mode-hook)
  369. (add-hook 'erc-insert-post-hook #'erc-truncate-buffer)
  370. (add-hook 'erc-text-matched-hook 'distopico:erc-matched-or-insert-hook)
  371. (add-hook 'erc-insert-modify-hook 'distopico:erc-matched-or-insert-hook)
  372. (add-hook 'erc-after-connect #'distopico:erc-after-connect-hook)
  373. (add-hook 'distopico:after-init-load-hook #'distopico:erc-init-load-hook)
  374. (provide 'conf-erc)