diff --git a/modules/app/irc/README.org b/modules/app/irc/README.org new file mode 100644 index 000000000..bea3ec72c --- /dev/null +++ b/modules/app/irc/README.org @@ -0,0 +1,19 @@ +* :app irc + +This module makes Emacs an irc client, using [[https://github.com/jorgenschaefer/circe][~circe~]]. + +** Dependencies + +I use ~pass~ to not have the passwords written in my account. It's available under ~:tools~ modules. +If you want to use TLS you also need =openssl= or =gnutls-cli=. + +Configure Emacs to use your favorite irc servers: +#+BEGIN_SRC emacs-lisp :tangle no +(set! :irc "chat.freenode.net" + `(:tls t + :nick "benny" + :sasl-username ,(password-store-get "irc/freenode") + :sasl-password ,(password-store-get "irc/freenode") + :channels ("#emacs"))) +#+END_SRC + diff --git a/modules/app/irc/autoload/evil.el b/modules/app/irc/autoload/evil.el new file mode 100644 index 000000000..8eb627682 --- /dev/null +++ b/modules/app/irc/autoload/evil.el @@ -0,0 +1,2 @@ +;;; app/irc/autoload/evil.el -*- lexical-binding: t; -*- + diff --git a/modules/app/irc/autoload/irc.el b/modules/app/irc/autoload/irc.el new file mode 100644 index 000000000..07e1a89d7 --- /dev/null +++ b/modules/app/irc/autoload/irc.el @@ -0,0 +1,18 @@ +;;; app/irc/autoload/email.el -*- lexical-binding: t; -*- + +;;;###autoload +(defun =irc () + "Connect to IRC." + (interactive) + (call-interactively #'circe)) + +;;;###autoload +(defun +irc/connect-all () + "Connect to all `:irc' defined servers." + (interactive) + ;; force a library load for +irc--accounts + (circe--version) + (mapcar (lambda (network) + (circe (car network))) + +irc--accounts)) + diff --git a/modules/app/irc/config.el b/modules/app/irc/config.el new file mode 100644 index 000000000..52bdeb335 --- /dev/null +++ b/modules/app/irc/config.el @@ -0,0 +1,158 @@ +;;; app/irc/config.el -*- lexical-binding: t; -*- + +;; +;; Config +;; + +(defvar +irc--accounts nil) + +(def-setting! :irc (server letvars) + "Registers an irc server for circe." + `(progn + (push ',(cons server letvars) +irc--accounts) + (push ',(cons server letvars) circe-network-options))) + +(defvar +irc-left-padding 13) + +(defvar +irc-disconnect-hook nil) + +(defvar +irc-bot-list '("fsbot" "rudybot")) + +(defvar +irc-time-stamp-format "%H:%M") + +(defvar +irc-notifications-watch-strings nil) + +;; +;; Plugins +;; + +(def-package! circe + ;; need a low impact function just to force an eval + :commands (circe circe--version) + :init + (setq circe-network-defaults nil) + :config + (evil-set-initial-state 'circe-channel-mode 'emacs) + (defun +irc|circe-format-padding (left right) + (s-concat (s-pad-left +irc-left-padding " " left) " │ " right)) + + (setq circe-use-cycle-completion t + circe-reduce-lurker-spam t + + circe-format-say (format "{nick:+%ss} │ {body}" +irc-left-padding) + circe-format-self-say circe-format-say + circe-format-action (format "{nick:+%ss} * {body}" +irc-left-padding) + circe-format-self-action circe-format-action + + circe-format-server-topic + (+irc|circe-format-padding "*** Topic" "{userhost}: {topic-diff}") + + circe-format-server-join-in-channel + (+irc|circe-format-padding "*** Join" "{nick} ({userinfo}) joined {channel}") + + circe-format-server-join + (+irc|circe-format-padding "*** Join" "{nick} ({userinfo})") + + circe-format-server-part + (+irc|circe-format-padding "*** Part" "{nick} ({userhost}) left {channel}: {reason}") + + circe-format-server-quit + (+irc|circe-format-padding "*** Quit" "{nick} ({userhost}) left IRC: {reason}]") + + circe-format-server-quit-channel + (+irc|circe-format-padding "*** Quit" "{nick} ({userhost}) left {channel}: {reason}]") + + circe-format-server-rejoin + (+irc|circe-format-padding "*** Re-join" "{nick} ({userhost}), left {departuredelta} ago") + + circe-format-server-nick-change + (+irc|circe-format-padding "*** Nick" "{old-nick} ({userhost}) is now known as {new-nick}") + + circe-format-server-nick-change-self + (+irc|circe-format-padding "*** Nick" "You are now known as {new-nick} ({old-nick})") + + circe-format-server-nick-change-self + (+irc|circe-format-padding "*** Nick" "{old-nick} ({userhost}) is now known as {new-nick}") + + circe-format-server-mode-change + (+irc|circe-format-padding "*** Mode" "{change} on {target} by {setter} ({userhost})") + + circe-format-server-lurker-activity + (+irc|circe-format-padding "*** Lurk" "{nick} joined {joindelta} ago")) + + (defun +irc*circe-truncate-nicks (orig-func keywords) + "If nick is too long, truncate it. Uses `+irc-left-padding' +to determine length." + (when (plist-member keywords :nick) + (let* ((long-nick (plist-get keywords :nick)) + (short-nick (s-left (- +irc-left-padding 1) long-nick))) + ;; only change the nick if it's needed + (unless (or (= +irc-left-padding + (length long-nick)) + (equal long-nick short-nick)) + (plist-put keywords :nick (s-concat short-nick "▶"))))) + (funcall orig-func keywords)) + (advice-add 'circe--display-add-nick-property :around #'+irc*circe-truncate-nicks) + + (defun +irc*circe-disconnect-hook (&rest _) + (run-hooks '+irc-disconnect-hook)) + (advice-add 'circe--irc-conn-disconnected :after #'+irc*circe-disconnect-hook) + + (add-hook '+irc-disconnect-hook (λ! (run-at-time "5 minute" nil #'circe-reconnect-all))) + + (defun +irc|circe-message-option-bot (nick &rest ignored) + (when (member nick +irc-bot-list) + '((text-properties . (face circe-fool-face + lui-do-not-track t))))) + (add-hook 'circe-message-option-functions #'+irc|circe-message-option-bot) + + (enable-circe-new-day-notifier) + (add-hook 'circe-server-connected-hook + (λ! (run-at-time "5 minutes" nil #'enable-circe-notifications))) + + (add-hook! 'circe-channel-mode-hook '(enable-circe-color-nicks + enable-lui-autopaste + turn-on-visual-line-mode))) + +(def-package! circe-color-nicks + :commands enable-circe-color-nicks + :config + (setq circe-color-nicks-min-constrast-ratio 4.5 + circe-color-nicks-everywhere t)) + +(def-package! circe-new-day-notifier + :commands enable-circe-new-day-notifier + :config (setq circe-new-day-notifier-format-message + (+irc|circe-format-padding + "*** Day" + "Date changed [{day}]"))) + +(def-package! circe-notifications + :commands enable-circe-notifications + :config (setq circe-notifications-watch-strings + +irc-notifications-watch-strings)) + +(def-package! lui + :commands lui-mode + :bind ((:lui-mode-map + ("C-u" . lui-kill-to-beginning-of-line))) + :config + (enable-lui-logging) + (defun +irc|lui-setup-margin () + (setq lui-time-stamp-position 'right-margin + lui-time-stamp-format +irc-time-stamp-format + right-margin-width (length (format-time-string lui-time-stamp-format)))) + (defun +irc|lui-setup-wrap () + (setq fringes-outside-margins t + word-wrap t + wrap-prefix (s-repeat (+ +irc-left-padding 3) " "))) + (add-hook! 'lui-mode-hook '(+irc|lui-setup-margin +irc|lui-setup-wrap)) + (when (featurep! :feature spellcheck) + (setq lui-flyspell-p t + lui-fill-type nil))) + +(def-package! lui-autopaste + :commands enable-lui-autopaste) + +(def-package! lui-logging + :commands enable-lui-logging) diff --git a/modules/app/irc/packages.el b/modules/app/irc/packages.el new file mode 100644 index 000000000..58b486c76 --- /dev/null +++ b/modules/app/irc/packages.el @@ -0,0 +1,5 @@ +;; -*- no-byte-compile: t; -*- +;;; app/irc/packages.el + +(package! circe) +(package! circe-notifications)