#+title:  Auxillary and Optional Applications
#+author: Howard X. Abrams
#+date:   2021-11-18
#+tags: emacs

A literate programming file for helper apps in Emacs.

#+begin_src emacs-lisp :exports none
  ;;; ha-aux-apps --- Configuring helper apps in Emacs. -*- lexical-binding: t; -*-
  ;;
  ;; © 2021-2023 Howard X. Abrams
  ;;   Licensed under a Creative Commons Attribution 4.0 International License.
  ;;   See http://creativecommons.org/licenses/by/4.0/
  ;;
  ;; Author: Howard X. Abrams <http://gitlab.com/howardabrams>
  ;; Maintainer: Howard X. Abrams
  ;; Created: November 18, 2021
  ;;
  ;; This file is not part of GNU Emacs.
  ;;
  ;; *NB:* Do not edit this file. Instead, edit the original literate file at:
  ;;            ~/src/hamacs/ha-aux-apps.org
  ;;       And tangle the file to recreate this one.
  ;;
  ;;; Code:
  #+end_src
* Introduction
The following applications are not needed. I alternate between trying to /stay in Emacs/ taking advantage of the consistent interface, and using a stand-alone app on my Workday computer.
* Social Media Apps
** Mastodon
Glad to see the 2FA feature is working on the [[https://codeberg.org/martianh/mastodon.el][mastodon.el]] project, and even more glad to see the great birdland diaspora arrive in the land of the toots.
#+begin_src emacs-lisp
  (use-package mastodon
    :straight (:host codeberg :repo "martianh/mastodon.el")
    :init
    (setq mastodon-instance-url "https://pdx.social"
          mastodon-active-user "howard"))
#+end_src

I would like a dedicate perspective to Mastodon, and I would like a leader key sequence that /expands/ will all options, once Mastodon is running in its own perspective:
#+begin_src emacs-lisp
  (use-package mastodon
    :config
    (ha-leader "a m" `("mastodon" . ,(lambda () (interactive) (ha-tab-bar-new "mastodon" #'mastodon))))

    (defun ha-mastodon-scroll-or-more ()
      "Scroll a window, and at the end, get more entries in timeline."
      (interactive)
      (condition-case nil
          (scroll-up-command)
        (end-of-buffer
         (mastodon-tl--more))))

    (use-package major-mode-hydra
      :config
      (major-mode-hydra-define mastodon-mode (:quit-key "q")
        ("Timelines"
         (("u" mastodon-tl-update "update")
          ("F" mastodon-tl-get-federated-timeline "Federated")
          ("H" mastodon-tl-get-home-timeline "Home")
          ("L" mastodon-tl-get-local-timeline "Local")
          ("T" mastodon-tl-get-tag-timeline "Hashtag"))
         "Specials"
         (("M" mastodon-notifications--get-mentions "Mentions")
          ("N" mastodon-notifications-get "Notifications")
          ("A" mastodon-tl-followed-tags-timeline "All Tags")
          ("S" mastodon-profile-view-bookmarks "Saved bookmarks")
          ("O" mastodon-profile-my-profile "My Profile"))
         "Post"
         (("c" mastodon-toot "Compose toot")
          ("e" mastodon-toot-edit-toot-at-point "Edit toot")
          ("t" mastodon-tl-thread "Read thread")
          ("r" mastodon-toot-reply "Reply")
          ("m" mastodon-tl-dm-user "Direct Msg")
          ("d" mastodon-toot-delete-toot "Delete"))
         "Toot"
         (("f" mastodon-toot-toggle-favourite "Favorite")
          ("b" mastodon-toot-toggle-boost "Boost")
          ("s" mastodon-toot-toggle-bookmark "Save")
          ("y" mastodon-toot-copy-toot-url "Copy URL")
          ("Y" mastodon-toot-copy-toot-text "Copy text"))
         "Navigation"
         (("n" mastodon-tl-next-tab-item "next" :color pink)
          ("p" mastodon-tl-previous-tab-item "previous" :color pink)
          ("," ha-mastodon-scroll-or-more "...more" :color pink))))))
#+end_src

Let’s turn on non-fixed-width fonts to make everything easier to read:
#+begin_src emacs-lisp
  (use-package mastodon
     :hook (mastodon-mode . mixed-pitch-mode))
#+end_src
#+end_src
** Matrix/Element
Yet another encrypted chat/VoIP client-server, but unlike Signal and Telegram, [[matrix.org][Matrix]] is act ually open source. In other words, a project for nerds. We’ll be using Alphapapa’s latest [[https://github.com/alphapapa/ement.el][ement]] project.
#+begin_src emacs-lisp
  (use-package ement
    :straight (:host github :repo "alphapapa/ement.el")
    :after major-mode-hydra
    :config
    (major-mode-hydra-define ement-room-mode (:quit-key "q")
      ("Send"
       (("c" ement-room-compose-message "Compose Message")
        ("d" ement-send-direct-message "Direct Message")
        ("S" ement-room-dispatch-reply-to-message "Message Reply")
        ("s" ement-room-send-message "Send (or <Return>)"))
       "Other"
       (("r" ement-room-send-reaction "React")
        ("e" ement-room-send-emote "Emote")
        ("R" ement-view-room "Jump to Room"))))

    (defun ha-ement-connect ()
      (interactive)
      (let* ((auth-results (auth-source-search :host "matrix"))
             (auth-first   (first auth-results))
             (username     (plist-get auth-first :user))
             (password     (funcall (plist-get auth-first :secret))))
        (ement-connect :user-id username
                       :password password
                       :uri-prefix "https://matrix.org"))
      (ha-leader
          "a x S" '("send" . ement-send-direct-message)
          "a x s" '("send" . ement-room-send-message)
          "a x c" '("compose" . ement-room-compose-message)
          "a x r" '("room" . ement-view-room)))

    (ha-leader
      "a x"   '(:ignore t :which-key "matrix")
      "a x x" '("connect" . ha-ement-connect)
      "a x s" '("send" . ement-send-direct-message)))
#+end_src

Perhaps we can make it more attractive:
#+begin_src emacs-lisp
  (use-package ement
    :hook (ement-room-mode . ha-textual-litagures)
    :custom-face
    (ement-room-self-message ((t (:inherit variable-pitch :height 1.0))))
    (ement-room-message-text ((t (:inherit variable-pitch :height 1.1 :foreground "#f08c60"))))
    :init
    (setq ement-save-sessions t
          ement-room-avatars nil
          ement-room-timestamp-format ""
          ement-room-send-message-filter 'ement-room-send-org-filter))
#+end_src
** Telega
I'm thinking the [[https://zevlg.github.io/telega.el/][Telega package]] would be better than Bitlbee for Telegram communication.

#+begin_src emacs-lisp :tangle no
  (use-package telega
    :config
    (setq telega-chat-show-avatars nil
          telega-use-tracking-for nil ; '(any pin unread)
          ;; Use org formatting for normal messages.
          ;; Want to send the org markup without formatting?
          ;; The `nil' sets that with `C-u' then Return:
          telega-chat-input-markups '("org" nil)
          telega-emoji-use-images t
                                          ; telega-completing-read-function #'ivy-completing-read
          telega-msg-rainbow-title nil)

    (when (fboundp 'evil-insert-state)
      (add-hook 'telega-chat-mode-hook 'evil-insert-state))

    (ha-leader "a t" `("telega" . ,(lambda () (interactive) (ha-tab-bar-new "telega" #'telega)))))
#+end_src
For some reason, you need [[https://github.com/Fanael/rainbow-identifiers][rainbow-identifiers]] to work, oh, I guess the docs state this.

In the Telega chat’s, let’s turn on non-fixed-width fonts:
#+begin_src emacs-lisp :tangle no
  (use-package telega
     :hook (telega-chat-mode . mixed-pitch-mode))
#+end_src
* RPG DM
Been working on my [[https://gitlab.com/howardabrams/emacs-rpgdm][RPG DM project]] for getting Emacs helping as a /Dungeon Master's Assistant/. The idea is to be able to roll dice and whatnot. What I find most useful is the [[https://gitlab.com/howardabrams/emacs-rpgdm/-/blob/main/rpgdm-tables.el][random tables]].
#+begin_src emacs-lisp :tangle no
  (when (f-directory? "~/src/emacs-rpgdm")
    (use-package rpgdm
      :straight (:local-repo "~/src/emacs-rpgdm")
      :commands (rpgdm-mode rpgdm-tables-load)
      :init (setq rpgdm-base (expand-file-name "~/src/emacs-rpgdm"))
      :config (ha-leader "t D" '("rpg dm" . rpgdm-mode))))
#+end_src

Working on my new replacement of my DM code:
#+begin_src emacs-lisp
  (when (f-directory? "~/src/emacs-rpgtk")
    (use-package rpgtk
      :straight (:local-repo "~/src/emacs-rpgtk")
      :after hydra
      ;; :commands (rpgtk-mode rpgtk-tables-load rpgtk-dice rpgtk-message)
      :custom
      (rpgtk-tables-directory (expand-file-name "~/src/emacs-rpgtk/tables"))

      :config
      (ha-leader
        "t D" '("rpg dm" . rpgtk-mode)
        "a d" '("rpg dm" . hydra-rpgtk/body))))
#+end_src


And my [[https://gitlab.com/howardabrams/emacs-ironsworn][new Ironsworn project]] expands on it, giving me both the Oracles and the Moves. With an Org file, I can easily play Solo:
#+begin_src emacs-lisp
  (when (f-directory? "~/src/emacs-ironsworn")
    (use-package rpgdm-ironsworn
      :after rpgdm
      :straight (:local-repo "~/src/emacs-ironsworn")
      :init
      (setq rpgdm-ironsworn-project (expand-file-name "~/src/emacs-ironsworn")
            ;; Ignore org links that call my RPG functions:
            org-link-elisp-skip-confirm-regexp (rx string-start (optional "(") "rpgdm-"
                                                   (or "tables-" "ironsworn-")
                                                   (one-or-more any)))))
#+end_src
The project is interesting, and I should record a tutorial how to use it.
* Playing Music
After building a NAS out of a Raspberry Pi to hold my /ripped/ CD collection, let’s control my playlists from Emacs.
A more /rich/ experience than the [[https://github.com/isamert/empv.el][empv]] project is ⁨Álvaro’s [[https://github.com/xenodium/ready-player][ready-player]]. First install [[https://mpv.io/][mpv]]:

#+BEGIN_SRC sh
  brew install mpv
#+END_SRC

And make sure we can play it:

#+BEGIN_SRC sh
  mpv "/Volumes/music/Steely Dan/Aja/01 Black Cow.mp3"
#+END_SRC

And stream my favorite local Jazz radio station:

#+BEGIN_SRC sh
  mpv --terminal --force-window=no --no-resume-playback \
      'https://ais-sa3.cdnstream1.com/2442_128.aac/playlist.m3u8' &
#+END_SRC

And then the configuration:

#+BEGIN_SRC emacs-lisp
  (use-package ready-player
    :straight (:type git :host github :repo "xenodium/ready-player")
    :config
    (when (eq system-type 'darwin)
      (set-fontset-font t nil "SF Pro Display" nil 'append)
      (ready-player-macos-use-sf-symbols))

    (setq ready-player-my-media-collection-location "/Volumes/music")

    (defun ha-ready-player-load-playlist ()
      "Open a playlist in the correct location."
      (interactive)
      (let ((default-directory
             (expand-file-name "Playlists/"
                               ready-player-my-media-collection-location)))
        (setq ready-player-shuffle t)
        (call-interactively 'ready-player-load-m3u-playlist)))

    (transient-append-suffix 'ready-player-menu "?"
      '("P" "Load Playlist" ha-ready-player-load-playlist))

    (defun ha-ready-player-load-directory ()
      "Open a directory in the correct location."
      (interactive)
      (let ((default-directory
             (file-name-as-directory ready-player-my-media-collection-location)))
        (setq ready-player-shuffle nil)
        (call-interactively 'ready-player-load-directory)))

    (transient-append-suffix 'ready-player-menu "d"
      '("A" "Play Album" ha-ready-player-load-directory))

    (ha-leader "a r" '("ready player"  . ready-player-menu))
    (ready-player-mode +1))
#+END_SRC

Since ready-player doesn’t support [[https://github.com/xenodium/ready-player/issues/36][adjusting the volume]], I can, at least, set the /default volume/ low enough that my speaker adjustment affects it:

#+BEGIN_SRC conf :tangle ~/.config/mpv/mpv.conf
  volume=60
#+END_SRC

Would love to have the /playing/ media in a particular tab-bar and in a particular location.

#+BEGIN_SRC emacs-lisp :tangle no
  (defun ha-ready-player-switch ()
    "Switch to the buffer window of the ready-player."
    (interactive)
    (let* ((buf-list
            (seq-filter (lambda (buf)
                          (string-match (rx string-start "ready-player: "
                                            (+ (not "*")) string-end)
                                        (buffer-name buf)))
                        (buffer-list)))
           (buffer (car buf-list))
           (tab    (tab-bar-get-buffer-tab (buffer-name buffer) t)))
      (if tab
          (tab-bar-switch-to-tab (alist-get 'name tab))
        (tab-bar-switch-to-tab "main"))))
#+END_SRC

* Technical Artifacts                                :noexport:
Let's =provide= a name so we can =require= this file:

#+begin_src emacs-lisp :exports none
  (provide 'ha-aux-apps)
  ;;; ha-aux-apps.el ends here
  #+end_src

#+description: A literate programming file for helper apps in Emacs.

#+property:    header-args:sh :tangle no
#+property:    header-args:emacs-lisp  :tangle yes
#+property:    header-args    :results none :eval no-export :comments no mkdirp yes

#+options:     num:nil toc:t todo:nil tasks:nil tags:nil date:nil
#+options:     skip:nil author:nil email:nil creator:nil timestamp:nil
#+infojs_opt:  view:nil toc:t ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js
