conf-jabber.el 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. ;;; Code:
  2. (require 'jabber)
  3. (require 'jabber-otr)
  4. (require 'autosmiley)
  5. (require 'visual-fill-column)
  6. ;; Costom vars
  7. (defcustom distopico:jabber-default-account "vxcamiloxv@disroot.org"
  8. "Jabber default account."
  9. :type 'string
  10. :group 'jabber)
  11. (defcustom distopico:jabber-default-nickname "distopico"
  12. "Jabber default Nickname."
  13. :type 'string
  14. :group 'jabber)
  15. (defcustom distopico:jabber-muc-exclude-regexp
  16. (concat "^\\bsubject\\b\:")
  17. "This regexp matches unwanted noise on jabber track muc."
  18. :type 'regexp
  19. :group 'jabber)
  20. (defcustom distopico:jabber-muc-list nil
  21. "Jabber muc list."
  22. :type 'alist
  23. :group 'jabber)
  24. ;; Control vars
  25. (defvar distopico:jabber-mode-line-format nil
  26. "String to display in the mode line.")
  27. (defvar distopico:jid-history '())
  28. (defvar distopico:jabber-account-alist nil)
  29. (defvar distopico:jabber-invalid-certificate-servers nil)
  30. ;; Basic
  31. (setq jabber-history-enabled t
  32. jabber-auto-reconnect t
  33. jabber-mode-line-mode t
  34. jabber-vcard-avatars-retrieve t
  35. jabber-avatar-max-width 40
  36. jabber-avatar-max-height 40
  37. jabber-backlog-number 40
  38. jabber-backlog-days 30
  39. ;; nil
  40. jabber-show-resources nil
  41. jabber-use-global-history nil
  42. jabber-show-offline-contacts nil
  43. jabber-message-alert-same-buffer nil
  44. ;; String
  45. jabber-default-status "M-x mode!!"
  46. jabber-roster-buffer "*jabber-roster*"
  47. jabber-groupchat-buffer-format "*jabber-room: [ %n ]*"
  48. jabber-chat-buffer-format "*jabber-chat: [ %n ]*"
  49. jabber-chat-time-format "%H:%M"
  50. jabber-chat-delayed-time-format "%H:%M"
  51. jabber-notifications-icon notifications-application-icon
  52. jabber-notifications-timeout -1
  53. ;; jabber-muc-default-nicknames "DistopicoVegan"
  54. jabber-roster-line-format " %4c %s | %a %-23n \n %8u %S" ;; " %c %-25n %u %-8s %S"
  55. jabber-history-dir (in-emacs-d ".cache/jabber-history")
  56. jabber-avatar-cache-directory (in-emacs-d ".cache/jabber-avatar-cache")
  57. jabber-otr-directory (in-emacs-d "config/jabber-otr")
  58. ;; Other
  59. jabber-alert-presence-hooks nil
  60. jabber-alert-message-hooks '(jabber-message-echo
  61. jabber-message-notifications
  62. jabber-message-scroll)
  63. jabber-alert-muc-hooks '(jabber-muc-echo-personal
  64. jabber-muc-notifications-personal
  65. jabber-muc-scroll)
  66. jabber-post-connect-hooks '(jabber-send-current-presence
  67. jabber-muc-autojoin
  68. jabber-whitespace-ping-start
  69. jabber-keepalive-start
  70. jabber-vcard-avatars-find-current)
  71. ;; Custom
  72. distopico:jabber-muc-list '("veganismo@salas.suchat.org"
  73. "vegan@conference.jabber.org"
  74. "elbinario@salas.xmpp.elbinario.net"
  75. "pump.io@conference.movim.eu"))
  76. ;; Jabber Accounts
  77. (ignore-errors
  78. (load (expand-file-name ".conf-private.gpg" "~/") t))
  79. (setq jabber-account-list distopico:jabber-account-alist
  80. jabber-invalid-certificate-servers distopico:jabber-invalid-certificate-servers)
  81. ;; Custom headers/model-line
  82. (setq jabber-chat-header-line-format
  83. '(" ❱ " (:eval (jabber-jid-displayname jabber-chatting-with))
  84. " " (:eval (let ((buddy (jabber-jid-symbol jabber-chatting-with)))
  85. (propertize
  86. (or (cdr (assoc (get buddy 'show) jabber-presence-strings))
  87. (get buddy 'show)) 'face
  88. (or (cdr (assoc (get buddy 'show) jabber-presence-faces))
  89. 'jabber-roster-user-online))))
  90. " • " (:eval (jabber-fix-status (get (jabber-jid-symbol
  91. jabber-chatting-with)
  92. 'status)))
  93. (:eval (when (not (eq jabber-events-message ""))
  94. " " jabber-events-message))
  95. (:eval (when (not (eq jabber-chatstates-message ""))
  96. " " jabber-chatstates-message)))
  97. jabber-muc-header-line-format
  98. '(" ❱ " (:eval (jabber-jid-displayname jabber-group))
  99. " • " jabber-muc-topic " ]"))
  100. ;; Custom keys
  101. (define-key jabber-roster-mode-map (kbd "C-q") 'distopico:jabber-close)
  102. (define-key jabber-chat-mode-map (kbd "C-q") 'distopico:jabber-close)
  103. (define-key jabber-chat-mode-map (kbd "C-c C-j") 'distopico:jabber-chat-bury)
  104. (define-key jabber-chat-mode-map (kbd "M-u") 'jabber-muc-names)
  105. (define-key jabber-common-keymap (kbd "C-<tab>") 'distopico:jabber-chat-with)
  106. (define-key jabber-common-keymap (kbd "C-x c") 'distopico:jabber-ido-switch-buffer)
  107. (define-key jabber-common-keymap (kbd "C-c C-r") 'distopico:jabber-reset-track-mode)
  108. (define-key jabber-common-keymap (kbd "C-c C-SPC") 'distopico:jabber-activity-switch-buffer)
  109. ;; Message alert hooks
  110. (define-jabber-alert echo "Show a message in the echo area (it not focus/active)"
  111. (lambda (text &optional title)
  112. (unless (minibuffer-prompt)
  113. (message "%s" (or title text)))))
  114. ;; Functions
  115. (defun distopico:jabber-display-roster ()
  116. "Open rosetr jabber in fullscreen and delete other windows."
  117. (interactive)
  118. (open-buffer-delete-others jabber-roster-buffer :jabber-fullscreen
  119. 'jabber-switch-to-roster-buffer))
  120. (defun distopico:jabber-close ()
  121. "Restore the previous window configuration and burry jabber buffer."
  122. (interactive)
  123. (bury-buffer-restore-prev :jabber-fullscreen))
  124. (defun distopico:jabber-chat-bury ()
  125. "Bury frame or delete frame if exit more than one."
  126. (interactive)
  127. (when (eq 'jabber-chat-mode major-mode)
  128. (if (< (length (frame-list)) 3)
  129. (progn
  130. (bury-buffer)
  131. (jabber-switch-to-roster-buffer))
  132. (delete-frame))))
  133. (defun distopico:jabber-roster-or-switch ()
  134. "Switch to last jabber activity or to roster if not have any activity."
  135. (interactive)
  136. (if (> (length jabber-activity-jids) 0)
  137. (progn
  138. (window-configuration-to-register :jabber-fullscreen)
  139. (distopico:jabber-activity-switch-buffer)
  140. (delete-other-windows))
  141. (distopico:jabber-display-roster)))
  142. (defun distopico:jabber-activity-switch-buffer ()
  143. "Switch to last jabber activity buffer."
  144. (when-let ((last-jid (car (distopico:jabber-last-jid-activity
  145. jabber-activity-jids))))
  146. (jabber-activity-switch-to last-jid)))
  147. (defun distopico:jabber-ido-switch-buffer ()
  148. "Switch to Jabber buffer using IDO to choose one."
  149. (interactive)
  150. (ido-for-mode "Jabber:" 'jabber-chat-mode))
  151. (defun distopico:jabber-reset-track-mode ()
  152. "Clears out annoying `jabber-activity-jids' stuff for when we don't care."
  153. (interactive)
  154. (setq jabber-activity-jids nil)
  155. (setq jabber-activity-personal-jids nil)
  156. (jabber-activity-mode-line-update))
  157. (defun distopico:jabber-chat-with (jid &optional other-window)
  158. "Ido-based jabber-chat-with variant using jabber `JID'.
  159. Optional open in `OTHER-WINDOW'."
  160. (interactive (list (distopico:jabber-read-jid-completing "Chat with: ")
  161. current-prefix-arg))
  162. (let* ((jc (distopico:jabber-jid-connection jid))
  163. (buffer (jabber-chat-create-buffer jc jid)))
  164. (if other-window
  165. (switch-to-buffer-other-window buffer)
  166. (switch-to-buffer buffer))))
  167. (defun distopico:jabber-auto-join (jc)
  168. "Only need to connect one `JC'.
  169. I use multi-account and default auto-join not work
  170. for me because connect all accounts."
  171. (let* ((state-data (fsm-get-state-data jc))
  172. (jid (plist-get state-data :original-jid)))
  173. (if (equal jid distopico:jabber-default-account)
  174. (progn
  175. (dolist (room distopico:jabber-muc-list)
  176. (jabber-groupchat-join jc room distopico:jabber-default-nickname))))))
  177. (defun distopico:jabber-muc-looks-ignore-p (message)
  178. "Return non-nil if jabber MESSAGE must be ignored."
  179. (if message
  180. (string-match distopico:jabber-muc-exclude-regexp message)
  181. t))
  182. (defun distopico:jabber-muc-looks-like-personal-p (message &optional group)
  183. "Return non-nil if jabber MESSAGE if I mentioned.
  184. Optional argument GROUP to look."
  185. (when message
  186. (string-match
  187. (concat "\\b" (regexp-quote (jabber-my-nick group)) "\\b")
  188. message)))
  189. (defun distopico:jabber-read-jid-completing (prompt)
  190. "Get JIDs candidates for ido completing, `PROMPT' to show in mini-buffer."
  191. (let* ((hist-items (remove-duplicates distopico:jid-history :test #'equal))
  192. (choices
  193. (mapcar #'symbol-name (jabber-concat-rosters))))
  194. (setf choices (append hist-items
  195. (sort (set-difference choices hist-items :test #'equal)
  196. #'string<)))
  197. (ido-completing-read prompt choices
  198. nil nil nil 'distopico:jid-history)))
  199. (defun distopico:jabber-jid-connection (jid)
  200. "Check if the `JID' has connection."
  201. (or (find-if
  202. #'(lambda (jc)
  203. (cl-find jid (plist-get (fsm-get-state-data jc) :roster)
  204. :key #'symbol-name
  205. :test #'equal))
  206. jabber-connections)
  207. (error "Cannot determine connection for %s" jid)))
  208. (defun distopico:jabber--activity-sort-jids (activity-jids)
  209. "Lookup jabber `ACTIVITY-JIDS' name and sort the last activity first."
  210. (let (result) (dolist (elt (mapcar #'jabber-activity-lookup-name
  211. activity-jids)
  212. result)
  213. (let ((sofar (assoc (jabber-jid-displayname (car elt)) result)))
  214. (unless sofar
  215. (push (cons (jabber-jid-displayname (car elt)) elt)
  216. result))))))
  217. (defun distopico:jabber-last-jid-activity (activity-jids)
  218. "Extract last jabber `JID' from `ACTIVITY-JIDS' list."
  219. (let* ((activity-sort-jids (distopico:jabber--activity-sort-jids activity-jids))
  220. (last-jid (car (cdr (car activity-sort-jids)))))
  221. (cons last-jid activity-sort-jids)))
  222. (defun distopico:jabber--activity-mode-line-format (activity-jids)
  223. "Format jabber `ACTIVITY-JIDS' to `mode-line' propertize.
  224. it's display the last JID's activity in the `mode-line' help popup,
  225. and jump to the last activity on click."
  226. (let* ((activity-count (length activity-jids))
  227. (last-activity (distopico:jabber-last-jid-activity activity-jids))
  228. (activity-sort-jids (cdr last-activity))
  229. (last-jid (car last-activity)))
  230. (jabber-propertize
  231. (number-to-string activity-count)
  232. 'face 'jabber-activity-face
  233. 'local-map (when (fboundp 'make-mode-line-mouse-map)
  234. (make-mode-line-mouse-map
  235. 'mouse-1 `(lambda ()
  236. (interactive "@")
  237. (jabber-activity-switch-to
  238. ,last-jid))))
  239. 'help-echo (concat "Activity: \n"
  240. (mapconcat
  241. (lambda (x)
  242. (let ((item (cdr x))
  243. (jid (car (cdr x)))
  244. (name (cdr (cdr x))))
  245. (concat "- " name ": " jid)))
  246. activity-sort-jids
  247. "\n")
  248. "\n\n"
  249. "Jump to "
  250. (jabber-jid-displayname last-jid)
  251. "'s buffer"))))
  252. (defun distopico:jabber--activity-mode-line-short ()
  253. "Jabber global `mode-line' with the summary of the activity in a short format.
  254. This only display the total of personal activity and general activity,
  255. and jump to the last activity on click."
  256. (if jabber-activity-jids
  257. (concat
  258. "💬("
  259. (mapconcat
  260. (lambda (activity) activity)
  261. (let ((activity-format '())
  262. (personal-jids jabber-activity-personal-jids)
  263. (general-jids (cl-remove-if-not
  264. (lambda (jid)
  265. (not (member jid jabber-activity-personal-jids)))
  266. jabber-activity-jids)))
  267. (when personal-jids
  268. (add-to-list
  269. 'activity-format
  270. (distopico:jabber--activity-mode-line-format personal-jids)))
  271. (when general-jids
  272. (add-to-list
  273. 'activity-format
  274. (distopico:jabber--activity-mode-line-format general-jids)))
  275. activity-format)
  276. "|")
  277. ") ")
  278. ""))
  279. (defun distopico:jabber--activity-mode-line-long ()
  280. "Jabber global `mode-line' with the summary of the activity in a long format.
  281. This display each jabber activity name in a list when own click action activity."
  282. (if jabber-activity-jids
  283. (concat (mapconcat
  284. (lambda (activity)
  285. (let ((item (cdr activity))
  286. (jid (car (cdr activity)))
  287. (name (cdr (cdr activity))))
  288. (jabber-propertize
  289. (concat "[" (truncate-string-to-width name 10 0 nil t) "]")
  290. 'face (if (member jid jabber-activity-personal-jids)
  291. 'jabber-activity-personal-face
  292. 'jabber-activity-face)
  293. 'local-map (when (fboundp 'make-mode-line-mouse-map)
  294. (make-mode-line-mouse-map
  295. 'mouse-1 `(lambda ()
  296. (interactive "@")
  297. (jabber-activity-switch-to
  298. ,jid))))
  299. 'help-echo (concat "Jump to "
  300. (jabber-jid-displayname jid)
  301. "'s buffer"))))
  302. (distopico:jabber--activity-sort-jids jabber-activity-jids)
  303. "")
  304. " ")
  305. ""))
  306. (defun distopico:jabber-chat-mode-hook ()
  307. "Hooks for `jabber-chat-mode'."
  308. (autosmiley-mode)
  309. (visual-line-mode +1)
  310. (visual-fill-column-mode +1)
  311. (auto-fill-mode +1))
  312. (defun distopico:jabber-init-load-hook ()
  313. "Hooks on init jabber for connect all accounts."
  314. (when jabber-account-list
  315. (run-at-time "30 sec" nil #'jabber-connect-all)))
  316. ;; Advice functions
  317. (defun distopico:jabber-muc-process-presence (jc presence)
  318. "Remove all muc notices based on the `PRESENCE', `JC' from original function.
  319. use this if you don't like all those notices about people joining/leaving."
  320. (let* ((from (jabber-xml-get-attribute presence 'from))
  321. (group (jabber-jid-user from))
  322. (buffer (get-buffer (jabber-muc-get-buffer group))))
  323. (when buffer
  324. (with-current-buffer buffer
  325. (ewoc-filter jabber-chat-ewoc
  326. (lambda (elt)
  327. (not (eq (car elt) :muc-notice))))))))
  328. (defun distopico:jabber-activity-add-muc (orig-fun &rest args)
  329. "Advice `ORIG-FUN' `jabber-activity-add-muc' for only personal mentions.
  330. Add a JID to mode line when `jabber-activity-show-p' needs from `ARGS' such as
  331. `NICK' name from the `GROUP' chat, also require `TEXT' to check if the message
  332. has name, Optional `BUFFER' and `PROPOSED-ALERT'"
  333. ;; nick group buffer text proposed-alert
  334. (let ((group (nth 1 args))
  335. (text (nth 3 args)))
  336. (when (funcall jabber-activity-show-p group)
  337. (unless (distopico:jabber-muc-looks-ignore-p text)
  338. ;; No need activity if no call nick or is a ignored message
  339. (add-to-list 'jabber-activity-jids group)
  340. (when (distopico:jabber-muc-looks-like-personal-p text group)
  341. (add-to-list 'jabber-activity-personal-jids group))
  342. (jabber-activity-mode-line-update)))))
  343. (defun distopico:jabber-activity-mode-line-update (orig-fun)
  344. "Advice `ORIG-FUN' `jabber-activity-mode-line-update' to avoid duplicate.
  345. Update the string shown in the mode line using `jabber-activity-make-string'
  346. on JIDs where `jabber-activity-show-p'.
  347. Optional not-nil GROUP mean that message come from MUC.
  348. Optional TEXT used with one-to-one or MUC chats and may be used to identify
  349. personal MUC message.
  350. Optional PRESENCE mean personal presence request or alert."
  351. (setq jabber-activity-mode-string (distopico:jabber--activity-mode-line-short))
  352. (setq distopico:jabber-mode-line-format (distopico:jabber--activity-mode-line-long))
  353. (setq jabber-activity-count-string
  354. (number-to-string (length jabber-activity-jids)))
  355. (force-mode-line-update 'all)
  356. (run-hooks 'jabber-activity-update-hook))
  357. (advice-add 'jabber-muc-process-presence :after #'distopico:jabber-muc-process-presence)
  358. (advice-add 'jabber-activity-add-muc :around #'distopico:jabber-activity-add-muc)
  359. (advice-add 'jabber-activity-mode-line-update :around #'distopico:jabber-activity-mode-line-update)
  360. ;; Hooks
  361. (add-hook 'kill-emacs-hook #'jabber-disconnect)
  362. (add-hook 'jabber-post-connect-hooks #'distopico:jabber-auto-join 'append)
  363. (add-hook 'jabber-chat-mode-hook #'distopico:jabber-chat-mode-hook 'append)
  364. (add-hook 'distopico:after-init-load-hook #'distopico:jabber-init-load-hook)
  365. (provide 'conf-jabber)
  366. ;;; conf-jabber.el ends here