<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>hi, it&#39;s mike</title>
    <link>https://mike.puddingtime.org/tags/mutt/</link>
    <description>Recent content on hi, it&#39;s mike</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <managingEditor>mike@puddingtime.org (mike)</managingEditor>
    <webMaster>mike@puddingtime.org (mike)</webMaster>
    <copyright>© 2026, mike</copyright>
    <lastBuildDate>Sun, 14 Jan 2024 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://mike.puddingtime.org/tags/mutt/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Daily Notes for 2024-01-14</title>
      <link>https://mike.puddingtime.org/posts/2024-01-14-daily-notes/</link>
      <pubDate>Sun, 14 Jan 2024 00:00:00 +0000</pubDate><author>mike@puddingtime.org (mike)</author>
      <guid>https://mike.puddingtime.org/posts/2024-01-14-daily-notes/</guid>
      <description>Junction, xdg-open, mutt, and html attachments. Launching Chrome directly into a profile.</description>
      <content:encoded><![CDATA[<h2 id="pdxtst">#pdxtst</h2>
<p>Wow, yesterday was nerve-wracking. Listening to the wind howl wasn&rsquo;t great before we lost power, and it was awful after we did around 2:30 in the afternoon. Plus there are all the things that start making noise once the power is out, like the UPS and smoke detectors. And because of the extreme cold, one of the motorcycle disc locks started going off every ten minutes or so until I went out and got it loose and brought it inside to thaw.</p>
<p>Due to whatever local peculiarities in the grid, we could look out any window and see lights on around us after the sun went down, and when the Portland General map was willing to load we could see the outage reports disappearing all around us until we were part of a little island clustered around the neighborhood park.</p>
<p>Power outages aren&rsquo;t rare in this neighborhood. I think before this one we&rsquo;ve never gone more than eight hours or so. This time the lights stayed out until 5:30. It got down to 44 in the house overnight, and would have been worse if not for the gas fireplace holding the line until we went to bed.</p>
<p>Anyhow &hellip; no, it wasn&rsquo;t great, but it was also not life in the RV across the street, or a tent a block away. We could still sleep in a bed, keep the phones charged, or take one of several friends up on an offer to come over if we needed to.</p>
<h2 id="junction-xdg-open-mutt-and-html-mail">Junction, xdg-open, mutt and HTML mail</h2>
<p>The whole &ldquo;I want to use a plaintext mail client on a GUI desktop thing&rdquo; has been messy for a super long time. Excavating my mutt mailcap, I can see a bunch of swings at it going back 15 years. I thiiiink I might have finally figured out a setup that&rsquo;s still got a few rough edges but seems stable.</p>
<p>I&rsquo;ve got <a href="https://github.com/sonnyp/Junction">Junction</a> installed from a flatpak, and I&rsquo;ve got it set as the default browser (in GNOME that&rsquo;s <code>Settings -&gt; Default Apps -&gt; Web</code>), and as the default for the HTML file type (<code>xdg-mime default re.sonny.Junction.desktop text/html</code>).</p>
<p>In my <code>~/.mutt/mailcap</code> I&rsquo;ve got:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">text/html; xdg-open %s &amp;&gt; /dev/null &amp;; nametemplate=%s.html
</span></span><span class="line"><span class="cl">application/*; xdg-open %s &amp;&gt; /dev/null &amp;;
</span></span><span class="line"><span class="cl">image/*; xdg-open %s &amp;&gt; /dev/null &amp;;</span></span></code></pre></div>
<p>mutt defaults to the plaintext version of a message when I open it, but I can tap <code>v</code> and select the <code>text/html</code> part to bring up Junction.</p>
<p>I tried living with <a href="https://w3m.sourceforge.net/">w3m</a> as my default text-mode browser for reading HTML mail inline, but once I started counting the number of times I went ahead and bailed to a GUI browser I realized it&rsquo;s not a great experience. So I just let it display the plaintext version first, which is usually more readable than whatever w3m is going to render.</p>
<p>I also tried using Firefox as my default GUI browser for HTML mail, but I use Firefox for personal stuff and Chrome for work stuff, so it&rsquo;s helpful to open an HTML mail in the browser that&rsquo;ll have the right cookies.</p>
<p>So the net of it is:</p>
<ul>
<li>Junction can grab links from apps and pop up a selector, so a link from work Slack can open in Chrome and a link from a personal email can open in Firefox.</li>
<li>Junction can send HTML mail to a chosen browser.</li>
</ul>
<h2 id="making-chrome-profiles-available-from-gnome-launcher-and-junction">Making Chrome profiles available from GNOME launcher and Junction</h2>
<p>When launched from Junction or the GNOME launcher Google Chrome will launch with a profile selector, which is fine: I have two profiles, but usually only use the work-related one. So as a matter of curiosity I wanted to see if I could get Junction to offer profiles instead of a generic Chrome icon. You just need to make <code>.desktop</code> files for each profile and tell Chrome the directory to launch from.</p>
<p>Running from Flatpak as I do, I don&rsquo;t think Chrome&rsquo;s quite &ldquo;normal&rdquo; so I made data directories for each of my profiles and then made a pair of <code>.desktop</code> files. They look like this:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">[Desktop Entry]
</span></span><span class="line"><span class="cl">Version=1.0
</span></span><span class="line"><span class="cl">Name=Personal Chrome
</span></span><span class="line"><span class="cl"># Only KDE 4 seems to use GenericName, so we reuse the KDE strings.
</span></span><span class="line"><span class="cl"># From Ubuntu&#39;s language-pack-kde-XX-base packages, version 9.04-20090413.
</span></span><span class="line"><span class="cl">GenericName=Web Browser
</span></span><span class="line"><span class="cl"># Gnome and KDE 3 uses Comment.
</span></span><span class="line"><span class="cl">Comment=Access the Internet
</span></span><span class="line"><span class="cl">Exec= flatpak run com.google.Chrome %U --user-data-dir=/home/mph/.config/google-chrome/pdxmph
</span></span><span class="line"><span class="cl">StartupNotify=true
</span></span><span class="line"><span class="cl">Terminal=false
</span></span><span class="line"><span class="cl">Icon=/home/mph/.local/share/icons/google-chrome.png
</span></span><span class="line"><span class="cl">Type=Application
</span></span><span class="line"><span class="cl">Categories=Network;WebBrowser;
</span></span><span class="line"><span class="cl">MimeType=application/pdf;application/rdf+xml;application/rss+xml;application/xhtml+xml;application/xhtml_xml;application/xml;image/gif;image/jpeg;image/png;image/webp;text/html;text/xml;x-scheme-handler/ftp;x-scheme-handler/http;x-scheme-handler/https;
</span></span><span class="line"><span class="cl">Actions=new-window;new-private-window;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">[Desktop Action new-window]
</span></span><span class="line"><span class="cl">Name=New Window
</span></span><span class="line"><span class="cl">Exec= flatpak run com.google.Chrome %U --user-data-dir=/home/mph/.config/google-chrome/pdxmph
</span></span><span class="line"><span class="cl">[Desktop Action new-private-window]
</span></span><span class="line"><span class="cl">Name=New Incognito Window
</span></span><span class="line"><span class="cl">Exec= flatpak run com.google.Chrome %U --user-data-dir=/home/mph/.config/google-chrome/pdxmph</span></span></code></pre></div>
<p>The &ldquo;Exec&rdquo; lines are the pertinent ones.</p>
<p>Once set up, and after running  <code>update-desktop-database ~/.local/share/applications</code>, Junction will pick them up as a pair of distinct browsers.</p>
<p>It&rsquo;s a little hacky, and I think if you are running Chrome from outside Flatpak your approach will vary &ndash; there may be proper data directories in your <code>.config</code> hierarchy somewhere.</p>
<h2 id="vampire-survivors">Vampire Survivors</h2>
<p>Still playing. When you get to the late levels (approaching 100) or late into the round (approaching 30 minutes) and your weapons and powerups are maxed, it is almost psychedelic: Just thousands of creatures all running into this whirling, swirling vortex of destruction. The sound of all the weapons and powerups and exploding bad guys reminds me of being in the slots section of a busy casino.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Daily notes for 2024-01-09 (mutt noodling edition)</title>
      <link>https://mike.puddingtime.org/posts/2024-01-09-daily-notes/</link>
      <pubDate>Tue, 09 Jan 2024 10:41:05 -0800</pubDate><author>mike@puddingtime.org (mike)</author>
      <guid>https://mike.puddingtime.org/posts/2024-01-09-daily-notes/</guid>
      <description>Multi-account, GPG-secure mutt configs. Mutt message scoring with Ruby, and score color-coding.</description>
      <content:encoded><![CDATA[<h2 id="multi-account-gpg-secured-mutt-config">Multi-account, GPG-secured mutt config</h2>
<p>I keep having to reinvent this every few years, and I always stitch it together from assorted sources, mostly because Google sort of shifts around now and then. So:</p>
<ul>
<li>Given a Gmail account with IMAP access turned on</li>
<li>Given a Fastmail account using IMAP</li>
<li>Given mutt, with your configuration in <code>~/.mutt</code> and with <code>muttrc</code> and <code>macros</code> files.</li>
<li>Given a working gpg config you can use to encrypt/decrypt</li>
</ul>
<p>There are all sorts of ways to handle mutt config for assorted providers. The examples here are working right now, in early 2024. They probably have bits of cruft and lint because my config has been a work in progress since some time in the late 20th century.</p>
<h3 id="overview">Overview</h3>
<p>You&rsquo;re making profiles to do this: One for each of your accounts that will hold account specific config information. If you currently have a monolith config in mutt, you can lift a lot of stuff out of it and move it into a profile, then source the profile in your main <code>muttrc</code>.</p>
<p>You&rsquo;re also going to make and encrypt a credential file for each account. Some people do this all in one file and use account hooks to make sure <code>imap_user</code>, <code>imap_password</code> and <code>smtp_password</code> are set correcctly depending on the account you&rsquo;re operating in. I chose to make a file for each account.</p>
<p>You&rsquo;re going to make macros that source the profiles when you want to switch between them.</p>
<h3 id="0-pre-config-with-gmail-and-fastmail">0. Pre-config with Gmail and Fastmail</h3>
<p>I&rsquo;m not going to go into a ton of detail here:</p>
<ul>
<li>Gmail needs to have less secure app access turned on. Find it in your account settings. If you&rsquo;re doing this for a work account, it may be your admin hasn&rsquo;t enabled this. Have fun fighting city hall, in that case.</li>
<li>If you have a GSuite admin, they need to have enabled all IMAP clients, not just OAuth ones.</li>
<li>If you have 2FA turned on with Google, you will need to enable an application password.</li>
</ul>
<p>For Fastmail:</p>
<ul>
<li>You need to have an app password set up for mutt. <code>Settings -&gt; Privacy and Security -&gt; Integrations -&gt; App passwords</code></li>
</ul>
<h3 id="1-the-profile-files">1. The profile files</h3>
<p>Make profile files for each of your accounts. I name them <code>workplace.profile</code>, <code>fastmail.profile</code>, etc. It doesn&rsquo;t matter there&rsquo;s no required convention. It&rsquo;s a good idea to use the first one as the template for the second one.</p>
<p>This is an example of my Fastmail profile. Note line 6:</p>
<p><code>source &quot;gpg -d ~/.mutt/passwords.gpg |&quot;</code></p>
<p>That&rsquo;s where your credentials will come from. I&rsquo;ll show that file next.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># -*- muttrc -*-</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Mutt sender profile : personal/default</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">unset</span> folder
</span></span><span class="line"><span class="cl"><span class="nb">set</span> <span class="nv">smtp_authenticators</span> <span class="o">=</span> <span class="s1">&#39;gssapi:login&#39;</span> <span class="c1"># fastmail needs this</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> <span class="nv">imap_authenticators</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">source</span> <span class="s2">&#34;gpg -d ~/.mutt/passwords.gpg |&#34;</span> 
</span></span><span class="line"><span class="cl"><span class="nb">set</span> <span class="nv">spoolfile</span> <span class="o">=</span> <span class="s2">&#34;imaps://imap.fastmail.com&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> <span class="nv">folder</span> <span class="o">=</span> <span class="s2">&#34;imaps://imap.fastmail.com/INBOX&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> <span class="nv">postponed</span><span class="o">=</span><span class="s2">&#34;+Drafts&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> <span class="nv">hostname</span><span class="o">=</span><span class="s2">&#34;yourdomain.com&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> <span class="nv">signature</span><span class="o">=</span> <span class="s2">&#34;~/.mutt/personal.sig&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> <span class="nv">from</span><span class="o">=</span> <span class="s2">&#34;Bob Jones &lt;bob@yourdomain.com&gt;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> <span class="nv">realname</span> <span class="o">=</span> <span class="s2">&#34;Bob Jones&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> <span class="nv">smtp_url</span> <span class="o">=</span> <span class="s2">&#34;smtps://bobjones@fastmail.com@smtp.fastmail.com:465&#34;</span> <span class="c1"># use your fastmail username, not your email address</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> <span class="nv">imap_user</span> <span class="o">=</span> <span class="s2">&#34;bobjones@fastmail.com&#34;</span> <span class="c1"># use your fastmail username here, too</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># set the status to show which profile I&#39;m using</span>
</span></span><span class="line"><span class="cl"><span class="nb">set</span> <span class="nv">status_format</span><span class="o">=</span> <span class="s2">&#34;-%r-Fastmail: %f [Msgs:%?M?%M/?%m%?n? New:%n?%?o? Old:%o?%?d? Del:%d?%?F? Flag:%F?%?t? Tag:%t?%?p? Post:%p?%?b? Inc:%b?%?l? %l?]---(%s/%S)-%&gt;-(%P)---\n&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">unmy_hdr *
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">my_hdr From: Bob Jones &lt;bob@yourdomain.com&gt;
</span></span><span class="line"><span class="cl">my_hdr Organization: yourdomain.com
</span></span><span class="line"><span class="cl">my_hdr Sender: Bob Jones &lt;bob@yourdomain.com&gt;
</span></span><span class="line"><span class="cl">my_hdr Return-Path: &lt;bob@yourdomain.com&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># clear the existing mailboxes list</span>
</span></span><span class="line"><span class="cl">unmailboxes *
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># load up mailboxes appropriate to this profile</span>
</span></span><span class="line"><span class="cl">mailboxes + <span class="s2">&#34;=Spam&#34;</span>
</span></span><span class="line"><span class="cl">mailboxes + <span class="s2">&#34;=disposable&#34;</span>
</span></span><span class="line"><span class="cl">mailboxes + <span class="s2">&#34;=Newsletters&#34;</span>
</span></span><span class="line"><span class="cl">mailboxes + <span class="s2">&#34;=Sent&#34;</span>
</span></span><span class="line"><span class="cl">mailboxes + <span class="s2">&#34;=Archive&#34;</span></span></span></code></pre></div>
<h3 id="2-make-credentials-files">2. Make credentials files</h3>
<p>For each account, you need to make a file for your credentials.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">set imap_user=bob@bobjones.com
</span></span><span class="line"><span class="cl">set imap_pass=&#34;klatu barada nikto&#34;
</span></span><span class="line"><span class="cl">set smtp_pass=&#34;klatu barada nikto&#34;</span></span></code></pre></div>
<p>Name it whatever. <code>passwords-accountname</code> works.</p>
<p>Once you&rsquo;ve created the file, encrypt it with gpg:</p>
<p><code>gpg -r your-gpg-key@yourdomain.com -e passwords-fastmail</code></p>
<p>Test it:</p>
<p><code>gpg -d passwords-fastmail.gpg</code></p>
<p>Then shred the plaintext original:</p>
<p><code>shred -u passwords-fastmail</code></p>
<p>Make sure that your profile (from the previous step) is sourcing the gpg file in line 6 of my example, e.g.</p>
<p><code>source &quot;gpg -d ~/.mutt/passwords-fastmail.gpg |&quot;</code></p>
<h3 id="3-do-a-quick-mid-config-check">3. Do a quick mid-config check</h3>
<p>Might as well test it now.  You can do that by sourcing one of your profiles in your <code>muttrc</code>:</p>
<p><code>source ~/.mutt/fastmail.profile</code></p>
<p>When you run mutt the first time in this login session, you should get a gpg prompt for your credentials so mutt can decrypt your password file and use it to log in.</p>
<p>If it&rsquo;s working, now&rsquo;s the time to make your second profile and credentials files using the above steps since it&rsquo;ll be good to know what they&rsquo;re all called for the next step, which is making macros.</p>
<h3 id="4-make-macros">4. Make macros</h3>
<p>I keep my macros in their own file under <code>~/.mutt</code> just to keep things modular. You can put these in your main <code>muttrc</code>. Whatever you prefer. If you have a separate file, make sure to source it in <code>muttrc</code>:</p>
<p><code>source ~/.mutt/macros</code></p>
<p>Now add something like this for each account:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">macro index .cf &#39;&lt;sync-mailbox&gt;&lt;enter-command&gt;source ~/.mutt/fastmail.profile&lt;enter&gt;&lt;change-folder&gt;!&lt;enter&gt;&#39;
</span></span><span class="line"><span class="cl">macro index .cg &#39;&lt;sync-mailbox&gt;&lt;enter-command&gt;source ~/.mutt/google.profile&lt;enter&gt;&lt;change-folder&gt;!&lt;enter&gt;&#39;</span></span></code></pre></div>
<p>That just does one last sync, then sources your profile, then changes folders to the inbox of that profile.</p>
<p>Restart mutt. From the index, if all is working correctly, the macro <code>.cf</code> will source your <code>fastmail.profile</code> and the macro <code>.cg</code> will source your <code>google.profile</code> file (both of which also source/decrypt their respective credential files).</p>
<h3 id="5-in-conclusion">5. In conclusion</h3>
<p>Once it&rsquo;s all wired up and running, you should be able to switch back and forth between accounts with just a few seconds of latency as the inbox syncs on exit and the new inbox syncs on login.</p>
<h2 id="the-pleasures-of-mutt">The pleasures of mutt</h2>
<p>I went on a mutt revival kick early last year. It remains a land of contrasts. I never end up sticking to it 100 percent of the time but instead prefer to use it as a quick triage tool: It&rsquo;s easy to make macros and keybindings that speed up inbox processing. Sometimes it&rsquo;s easier to just bail out to the web mail interface, but during the day it&rsquo;s helpful to just burn through the inbox never taking my hands off the keyboard.</p>
<h2 id="mutt-scoring-and-color-treatments">mutt scoring and color treatments</h2>
<p>One last thing, I guess, since I&rsquo;m documenting stuff.  One of the reasons I like mutt for triage so much is my ability to add a little visual treatment to messages based on their scores. That makes it easy to see what in my inbox has more priority.</p>
<p>I&rsquo;ve got this little script in my <code>~/.mutt</code>:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="ch">#!/usr/bin/env ruby</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;mail&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;tempfile&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Wants a +/- integer, e.g. +20</span>
</span></span><span class="line"><span class="cl"><span class="n">score</span> <span class="o">=</span> <span class="no">ARGV</span><span class="o">.</span><span class="n">first</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">score_file</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="no">Dir</span><span class="o">.</span><span class="n">home</span><span class="si">}</span><span class="s2">/.mutt/scored&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">msg</span> <span class="o">=</span> <span class="no">Tempfile</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s1">&#39;msg&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">msg</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="vg">$stdin</span><span class="o">.</span><span class="n">read</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">mail</span> <span class="o">=</span> <span class="no">Mail</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="o">=</span> <span class="n">mail</span><span class="o">.</span><span class="n">from</span><span class="o">.</span><span class="n">first</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">File</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">score_file</span><span class="p">,</span> <span class="s2">&#34;a&#34;</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span> <span class="n">f</span><span class="o">.</span><span class="n">write</span> <span class="s2">&#34;score ~f</span><span class="si">#{</span><span class="n">from</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">score</span><span class="si">}</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">msg</span><span class="o">.</span><span class="n">close</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">msg</span><span class="o">.</span><span class="n">unlink</span></span></span></code></pre></div>
<p>And I&rsquo;ve got these macros in my <code>~/.mutt/macros</code> file:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># Score messages</span>
</span></span><span class="line"><span class="cl">macro index,browser .sp <span class="s2">&#34;&lt;pipe-entry&gt;~/.mutt/mailscore.rb +5\n&lt;enter-command&gt;source ~/.mutt/scored&lt;enter&gt;&#34;</span> <span class="c1"># score sender +5</span>
</span></span><span class="line"><span class="cl">macro index,browser .sP <span class="s2">&#34;&lt;pipe-entry&gt;~/.mutt/mailscore.rb +20\n&lt;enter-command&gt;source ~/.mutt/scored&lt;enter&gt;&#34;</span> <span class="c1"># score sender +20</span>
</span></span><span class="line"><span class="cl">macro index,browser .sm <span class="s2">&#34;&lt;pipe-entry&gt;~/.mutt/mailscore.rb -5\n&lt;enter-command&gt;source ~/.mutt/scored&lt;enter&gt;&#34;</span> <span class="c1"># score sender -5</span>
</span></span><span class="line"><span class="cl">macro index,browser .sM <span class="s2">&#34;&lt;pipe-entry&gt;~/.mutt/mailscore.rb -20\n&lt;enter-command&gt;source ~/.mutt/scored&lt;enter&gt;&#34;</span> <span class="c1"># score sender -20</span></span></span></code></pre></div>
<p>And I&rsquo;ve got a few lines in my <code>~/.mutt/colors</code> file:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">color index cyan default <span class="s2">&#34;~n 0-2 !~p&#34;</span>
</span></span><span class="line"><span class="cl">color index magenta default <span class="s2">&#34;~n &lt;5&#34;</span>
</span></span><span class="line"><span class="cl">color index brightyellow default <span class="s2">&#34;~n &gt;15&#34;</span>
</span></span><span class="line"><span class="cl">color index brightred default <span class="s2">&#34;~n &gt;19&#34;</span>
</span></span><span class="line"><span class="cl"><span class="c1">#</span></span></span></code></pre></div>
<p>The macros pipe a given message into the script, the script extracts the sender, and the script writes a line into my <code>~/.mutt/scored</code> file. Then the <code>~/.mutt/colors</code> file (which you need to source in <code>muttrc</code>) assigns colors to certain scores. I have a few other rules in <code>~/.mutt/scores</code>, as well:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># Date-based scoring penalties -- older things fall down</span>
</span></span><span class="line"><span class="cl">score ~d&gt;3d -1
</span></span><span class="line"><span class="cl">score ~d&gt;7d -3
</span></span><span class="line"><span class="cl">score ~d&gt;14d -10
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">score <span class="s2">&#34;~O&#34;</span> +10 <span class="c1"># old = +10 so I don&#39;t miss it</span>
</span></span><span class="line"><span class="cl">score <span class="s2">&#34;~F&#34;</span> +20 <span class="c1"># flagged = +20 so it stays in the interesting view for a while, even if old</span>
</span></span><span class="line"><span class="cl">score <span class="s2">&#34;!~p ~d&gt;7d&#34;</span> -10 <span class="c1"># not for me directly, getting old, let it fade away</span>
</span></span><span class="line"><span class="cl">score <span class="s2">&#34;!~l&#34;</span> +2 <span class="c1"># to a known list, give it a bump</span></span></span></code></pre></div>
]]></content:encoded>
    </item>
    <item>
      <title>Succumbed to mu.</title>
      <link>https://mike.puddingtime.org/posts/2023-04-17-succumbed-to-mu-dot/</link>
      <pubDate>Mon, 17 Apr 2023 00:00:00 +0000</pubDate><author>mike@puddingtime.org (mike)</author>
      <guid>https://mike.puddingtime.org/posts/2023-04-17-succumbed-to-mu-dot/</guid>
      <description>This had to happen eventually.</description>
      <content:encoded><![CDATA[<p>I had some futzing time before writing time this morning, so I succumbed to what I initially thought was just getting <a href="https://www.offlineimap.org/">OfflineIMAP</a> going so I could get some speed improvements from mutt. Then I noticed lbdb had a <a href="https://github.com/djcb/mu">mu</a> backend, which would mean address lookup for 20 years of email. And that meant my <code>packages.el</code> file had that <code>;;(mu4e +org)</code> just sitting there, waiting to be uncommented.</p>
<p>I have a little self-control, so I made myself wait until I had mutt working before trying out <a href="https://www.djcbsoftware.nl/code/mu/mu4e.html">mu4e</a>.</p>
<p>And at that point, there was a yak capering about in my living room, and it had to be shaved:</p>
<ul>
<li>mu</li>
<li>msmtp</li>
<li>mu4e</li>
<li>&hellip; and isync</li>
</ul>
<p>isync because I found a bug in OfflineIMAP, which was inevitable given the 20-year-old email corpus I&rsquo;ve been lugging around between mail servers. It was killing Homebrew&rsquo;s ability to run offlineimap daemonized, so down that rabbit hole I went to satisfy myself the problem was widespread enough that it was worth considering alternatives, and isync has some good press.</p>
<p>Initial impressions:</p>
<ul>
<li>mu is pretty cool on its own. Very curious to play with my 20 years of email with it.</li>
<li>I haven&rsquo;t really tested mu&rsquo;s performance under mutt with search. But it is nice having very fast address lookup.</li>
<li>msmtp is &hellip; fine? I was using built-in SMTP for mutt, msmtp is there for mu4e&rsquo;s benefit so I just use it for both clients now.</li>
<li>isync seems fast enough and works well once the initial mail store is read into maildir.</li>
</ul>
<p>Tomorrow I&rsquo;ll move the data and databases over to my desktop</p>
<p>Oh &hellip; mu4e.</p>
<p>It is weird, but it&rsquo;s my kind of weird. Very high concept. Super fast. The &ldquo;squee&rdquo; factor is being able to compose mail in org-mode markup, which it turns into multipart mail. If you read the HTML version of your mail, mu4e messages look like rich text. If you read the plaintext version, it applies some plaintext styling:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Heading
</span></span><span class="line"><span class="cl">═══════
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">*Bold* and /italics/ and [a link].
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">[a link] &lt;https://mike.puddingtime.org&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Subheading
</span></span><span class="line"><span class="cl">──────────</span></span></code></pre></div>
<p>Search is superfast, navigating between folders is keyboard-centric, and because the mail retrieval stuff is happening outside of an Emacs thread, it doesn&rsquo;t block. I think you can set it up to run isync async inside Emacs, but I just noticed someone&rsquo;s config a few minutes ago and it&rsquo;s bedtime.</p>
<p>I am not sure what its future will be. I really like my mutt setup. I wonder if there&rsquo;s a way to get the org-mode mail composition using Emacs as my mutt mail editor. Science needs to be done.</p>
]]></content:encoded>
    </item>
    <item>
      <title>An org-contacts source for lbdb</title>
      <link>https://mike.puddingtime.org/posts/2023-04-16-an-org-contacts-source-for-lbdb/</link>
      <pubDate>Sun, 16 Apr 2023 00:00:00 +0000</pubDate><author>mike@puddingtime.org (mike)</author>
      <guid>https://mike.puddingtime.org/posts/2023-04-16-an-org-contacts-source-for-lbdb/</guid>
      <description>I modified a Perl lbdb backend by ‪@publicvoit@graz.social ‬to use my org-contacts with mutt</description>
      <content:encoded><![CDATA[<p>This is a ruby-based back-end for <a href="https://www.spinnaker.de/lbdb/">The Little Brother&rsquo;s Database (lbdb)</a> that looks at a hard-coded <code>org-contacts</code> file. The idea comes from <a href="https://lists.gnu.org/archive/html/emacs-orgmode/2011-10/msg01059.html">a 2011 Perl implementation by Karl Voit</a>. Because I am not a Perl person, when it didn&rsquo;t work out of the box I converted it to Ruby.</p>
<p>And because I&rsquo;ve chosen to treat org-contacts as <code>TODO</code> items for purposes of remembering who to <code>PING</code>, <code>FOLLOWUP</code>, <code>SKED</code>, etc. it has to take the extra step of stripping those keywords from the returned name. Otherwise, my mails to Joe Grudd would be addressed to <code>FOLLOWUP Joe Grudd</code>.</p>
<p>That&rsquo;s a small bit of inelegance in my plaintext CRM setup:  As I&rsquo;ve figured out more about how <code>org-super-agenda</code> works, I&rsquo;ve had glimpses of how the plaintext CRM metadata could just be content in the <code>:PROPERTIES:</code> drawer, and hence invisible for purposes of tools like this, but a few other pieces of passive automation would have to become some sort of org-mode hook and I&rsquo;d lose the utility of tools like Beorg, which can&rsquo;t provide the automation of native Emacs.</p>
<p>For now, it&rsquo;s one of those &ldquo;this design isn&rsquo;t the cleanest, but it&rsquo;s simple and only creates a few easily solved problems&rdquo; things.</p>
<p>Here&rsquo;s the script itself. I put it in <code>~/bin</code> as <code>orgcontact.rb</code>:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="ch">#!/usr/bin/env ruby</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Get the query string</span>
</span></span><span class="line"><span class="cl"><span class="n">query</span> <span class="o">=</span> <span class="no">ARGV</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Set the path to your Org-contacts file</span>
</span></span><span class="line"><span class="cl"><span class="n">orgmodefile</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;HOME&#39;</span><span class="o">]</span><span class="si">}</span><span class="s2">/org/contacts.org&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Read in the whole contact file</span>
</span></span><span class="line"><span class="cl"><span class="n">raw_contacts</span> <span class="o">=</span> <span class="no">File</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">orgmodefile</span><span class="p">)</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;</span><span class="se">\n</span><span class="s2">** &#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Iterate through each contact</span>
</span></span><span class="line"><span class="cl"><span class="n">raw_contacts</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">contact</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="n">contact</span><span class="o">.</span><span class="n">match?</span><span class="p">(</span><span class="sr">/</span><span class="si">#{</span><span class="n">query</span><span class="si">}</span><span class="sr">/i</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Extract the name and email from the contact</span>
</span></span><span class="line"><span class="cl">    <span class="nb">name</span> <span class="o">=</span> <span class="n">contact</span><span class="o">[</span><span class="sr">/^[^\n]*/</span><span class="o">].</span><span class="n">gsub</span><span class="p">(</span><span class="sr">/(PING|INVITE|WRITE|PINGED|FOLLOWUP|SKED|NOTES|SCHEDULED|TIMEOUT|OK)\s+/i</span><span class="p">,</span> <span class="s2">&#34;&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">strip</span>
</span></span><span class="line"><span class="cl">    <span class="n">email</span> <span class="o">=</span> <span class="n">contact</span><span class="o">[</span><span class="sr">/:EMAIL:\s+(.*)$/</span><span class="p">,</span> <span class="mi">1</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Remove tags from the name</span>
</span></span><span class="line"><span class="cl">    <span class="nb">name</span><span class="o">.</span><span class="n">gsub!</span><span class="p">(</span><span class="sr">/:\S+:/</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">puts</span> <span class="s2">&#34;</span><span class="si">#{</span><span class="n">email</span><span class="si">}</span><span class="se">\t</span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="se">\t</span><span class="s2">(org-contacts)&#34;</span> <span class="k">if</span> <span class="nb">name</span> <span class="o">&amp;&amp;</span> <span class="n">email</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div>
<p>Adding <code>m_org_contacts</code> to the <code>METHODS</code> setting then including a little wrapper in <code>~/.lbdbrc</code> doesn&rsquo;t follow the canonical advice on how to configure an lbdb backend, but it works, and it&rsquo;s one less file to put somewhere:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">METHODS=&#34;m_osx_addressbook m_org_contacts&#34;
</span></span><span class="line"><span class="cl">MODULES_PATH=&#34;$MODULES_PATH $HOME/bin/lbdb&#34;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">m_org_contacts_query() {
</span></span><span class="line"><span class="cl">   ~/bin/orgcontact.rb &#34;$1&#34;
</span></span><span class="line"><span class="cl">}</span></span></code></pre></div>
<p>So, the outcome is just:</p>
<ol>
<li>
<p>Start a new message in mutt and start typing the name/address/etc.</p>
</li>
<li>
<p>lbdb provides a list of matches from a few sources I&rsquo;ve set up: org-contacts and macOS address book</p>
</li>
<li>
<p><code>ENTER</code> to select a candidate</p>
<p>b</p>
</li>
</ol>
]]></content:encoded>
    </item>
    <item>
      <title>Macros to score mail in mutt</title>
      <link>https://mike.puddingtime.org/posts/2023-04-09-macros-to-score-mail-in-mutt/</link>
      <pubDate>Sun, 09 Apr 2023 10:11:27 -0700</pubDate><author>mike@puddingtime.org (mike)</author>
      <guid>https://mike.puddingtime.org/posts/2023-04-09-macros-to-score-mail-in-mutt/</guid>
      <description>Some ruby wired up to mutt macros allows for on-the-fly sender scoring and a color-coded message index.</description>
      <content:encoded><![CDATA[<p>In my early mutt days I used procmail and SpamAssassin on my desktop machines. I had a bunch of mutt macros set up to help score email and mark things as spam or not-spam. Once I started using IMAP on someone else&rsquo;s server that all fell by the wayside, but I really missed being able to score mail while I was processing it. I use Fastmail these days, and they support Sieve scripts, but haven&rsquo;t chosen to expose the API for that. That&rsquo;s too bad.</p>
<p>Mutt does, however, have pretty good facilities for scoring individual properties of a message, then do different color treatments for a given message&rsquo;s score in the index. Scoring and coloring can be driven by mutt&rsquo;s <a href="https://www.sendmail.org/~ca/email/mutt/manual-4.html#ss4.1">extensive list of search patterns.</a>.</p>
<p>This is a lightly annotated selection from the top of my mutt scores file, which I source in <code>~/.mutt/muttrc</code> at startup:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"># -*- mode: conf-unix -*-
</span></span><span class="line"><span class="cl"># Mutt scoring file
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">unscore * # start fresh
</span></span><span class="line"><span class="cl">score ~p +5 # Mail addressed to me or one of my alternates gets 5 points
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"># Date-based scoring penalties -- older things fall down
</span></span><span class="line"><span class="cl">score ~d&gt;3d -1
</span></span><span class="line"><span class="cl">score ~d&gt;7d -3
</span></span><span class="line"><span class="cl">score ~d&gt;14d -10
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">score &#34;~O&#34; +10 # &#34;Old&#34; in mutt is &#34;seen but unread&#34;. +10 so I don&#39;t miss it
</span></span><span class="line"><span class="cl">score &#34;~F&#34; +20 # flagged = +20 so it stays in the interesting view for a while, even if older
</span></span><span class="line"><span class="cl">score &#34;!~p ~d&gt;7d&#34; -10 # not for me directly, getting old, let it fade away
</span></span><span class="line"><span class="cl">score &#34;!~l&#34; +2 # to a known list, give it a bump</span></span></code></pre></div>
<p>Given some scores, you can add color treatments:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">color index white default &#34;~n 6-9&#34;
</span></span><span class="line"><span class="cl">color index white red &#34;~f trimet ~s Service\\ Alert&#34;
</span></span><span class="line"><span class="cl">color index brightblack default &#34;~f trimet ~s Service\\ Alert ~d&gt;1d&#34;
</span></span><span class="line"><span class="cl">color index magenta default &#34;~n &lt;5&#34;
</span></span><span class="line"><span class="cl">color index brightyellow default &#34;~n &gt;10&#34;
</span></span><span class="line"><span class="cl">color index brightred default &#34;~n &gt;19&#34;</span></span></code></pre></div>
<p>Adding a score to the scores file manually is no big deal. Mine tends to include specific people, work email lists, things I definitely don&rsquo;t want to miss (e.g. service alerts from TriMet), and things that I should keep seeing but want to deemphasize (like business messages from hosting providers). But it&rsquo;d also be nice to do some scoring as I&rsquo;m reviewing mail to deal with new important senders, necessary nuisances, etc.</p>
<p>So I wrote some ruby and mutt macros that let me pipe a given message into a script that extracts the sender and writes a +/- modifier to their score in a <code>scored</code> file I source alongside my <code>scores</code> file.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="ch">#!/usr/bin/env ruby</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;mail&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;tempfile&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Wants a +/- integer, e.g. +20</span>
</span></span><span class="line"><span class="cl"><span class="n">score</span> <span class="o">=</span> <span class="no">ARGV</span><span class="o">.</span><span class="n">first</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">score_file</span> <span class="o">=</span> <span class="s2">&#34;/Users/mph/.mutt/scored&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">msg</span> <span class="o">=</span> <span class="no">Tempfile</span><span class="o">.</span><span class="n">new</span><span class="p">(</span><span class="s1">&#39;msg&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">msg</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="vg">$stdin</span><span class="o">.</span><span class="n">read</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">mail</span> <span class="o">=</span> <span class="no">Mail</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">from</span> <span class="o">=</span> <span class="n">mail</span><span class="o">.</span><span class="n">from</span><span class="o">.</span><span class="n">first</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">File</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">score_file</span><span class="p">,</span> <span class="s2">&#34;a&#34;</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span> <span class="n">f</span><span class="o">.</span><span class="n">write</span> <span class="s2">&#34;score ~f</span><span class="si">#{</span><span class="n">from</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">score</span><span class="si">}</span><span class="se">\n</span><span class="s2">&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">msg</span><span class="o">.</span><span class="n">close</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">msg</span><span class="o">.</span><span class="n">unlink</span></span></span></code></pre></div>
<p>Pretty much just &ldquo;parse the mail for the first <code>from</code> address, wrap it in a mutt <code>score</code> stanza, tack that on to the end of <code>scored</code>.&rdquo;</p>
<p>Then I added some macros:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"># Score messages
</span></span><span class="line"><span class="cl">macro index,browser .sp &#34;&lt;pipe-entry&gt;~/.mutt/mailscore.rb +5\n&lt;enter-command&gt;source ~/.mutt/scored&lt;enter&gt;&#34; # score sender +5
</span></span><span class="line"><span class="cl">macro index,browser .sP &#34;&lt;pipe-entry&gt;~/.mutt/mailscore.rb +20\n&lt;enter-command&gt;source ~/.mutt/scored&lt;enter&gt;&#34; # score sender +20
</span></span><span class="line"><span class="cl">macro index,browser .sm &#34;&lt;pipe-entry&gt;~/.mutt/mailscore.rb -5\n&lt;enter-command&gt;source ~/.mutt/scored&lt;enter&gt;&#34; # score sender -5
</span></span><span class="line"><span class="cl">macro index,browser .sM &#34;&lt;pipe-entry&gt;~/.mutt/mailscore.rb -20\n&lt;enter-command&gt;source ~/.mutt/scored&lt;enter&gt;&#34; # score sender -20</span></span></code></pre></div>
<p>They just pipe the message into the Ruby script with a scoring argument, the script modifies <code>scored</code>, then the macro re-sources <code>scored</code> so the index reflects the new scoring.</p>
<p>Tapping <code>.sp</code> (score plus) in the index scores a sender up, <code>.sm</code> (score minus) scores them down. <code>.sP</code> and <code>.sM</code> are embiggened scores.</p>
<p>The way mutt works, it doesn&rsquo;t accumulate these pluses and minuses, so when it parses the <code>scored</code> file it&rsquo;s just going to use the last modifier I added. That seems fine, since they&rsquo;re broad and there are other scoring factors at work. Over time the file will get long. Maybe the right thing to do is read the file in, try to match on a sender, and overwrite their entry, just to cut down on duplication.</p>
<p>The net effect of the current system is that the index is color-coded into four broad tiers: The super important, the pretty important, kinda average and should be read, and not particularly interesting and maybe worth an unsubscribe.</p>
]]></content:encoded>
    </item>
    <item>
      <title>Daily notes for 2023-04-08</title>
      <link>https://mike.puddingtime.org/posts/2023-04-08-daily-notes-for-2023-04-08/</link>
      <pubDate>Sat, 08 Apr 2023 10:26:49 -0700</pubDate><author>mike@puddingtime.org (mike)</author>
      <guid>https://mike.puddingtime.org/posts/2023-04-08-daily-notes-for-2023-04-08/</guid>
      <description>As always, a plaintext revival means a mutt revival.</description>
      <content:encoded><![CDATA[<h3 id="these-things-come-in-waves">These things come in waves</h3>
<p>In the past, when I&rsquo;ve gone through a plaintext or Emacs kick of some kind, I&rsquo;ve blown past what I&rsquo;ve come to think of as a common sense limit: Eventually I&rsquo;m looking at Wanderlust, GNUS, or mu4e and thinking about the whole email in Emacs thing. Inevitably, then, you&rsquo;re looking at some sort of way to sync your IMAP account down to your local machine, and a whole layer goes in to make that work.</p>
<p>I walked up to the line this time around, asked myself what problem I&rsquo;m trying to solve, and remembered that part of what is making Doom Emacs work for me right now is how much I&rsquo;ve been keeping things limited to stuff that has to do with writing and stuff that has to do with personal organization. Those are things that don&rsquo;t stress Emacs&rsquo; single-threaded nature the way I do them, and that don&rsquo;t take me into the murky space between Emacs and the OS.</p>
<p>The problem I am trying to solve whenever I go on these kicks, is the pain of getting parts of the macOS experience into a more keyboard-centric place. Mail always sticks out because I don&rsquo;t like doing Mac Mail from the keyboard. There&rsquo;s <a href="https://smallcubed.com/">Mail Act-On</a>, but at $45 a renewal it&rsquo;s just more than I can see paying, and it doesn&rsquo;t really do much for visualization &ndash; it just makes it easy to move things into folders.</p>
<p>So my mind always goes to <a href="http://www.mutt.org">mutt</a>. It&rsquo;s not perfect &ndash; search is a challenge without something external augmenting it &ndash; but I&rsquo;m not sure it matters in the end: I use Fastmail for IMAP, and I don&rsquo;t mind punting to its web interface if I need to go dig for something.</p>
<p>I thought about mutt this time because my long-standing config has always sort of <em>felt</em> similar to the way Spacemacs and Doom put a modal UI over Emacs.  My mutt macros &ndash; here&rsquo;s a sample &ndash; use <code>.</code> as a leader and are written mnemonically:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">macro index .sn &#34;l ^a^k~N\n&#34; # Show unread/new only
</span></span><span class="line"><span class="cl">macro index .sa &#34;l ^a^kall\n&#34; # show all
</span></span><span class="line"><span class="cl">macro index .C &#34;&lt;esc&gt;V&#34; # toggle threads
</span></span><span class="line"><span class="cl">macro index .si &#34;l (~n5-100|~N)\n&#34; # show interesting 
</span></span><span class="line"><span class="cl">macro index .rs &#34;&lt;enter-command&gt;source ~/.mutt/scores&lt;enter&gt;&#34; # reprocess scores
</span></span><span class="line"><span class="cl">macro index .sf &#34;l ~F\n&#34; # show flagged
</span></span><span class="line"><span class="cl">macro index .hl &#34;l ^a^k!~l\n&#34; # hide lists
</span></span><span class="line"><span class="cl">macro index .to &#34;T ~d&gt;7d\n&#34; # &#34;tag old&#34; -- messages older than 7 days
</span></span><span class="line"><span class="cl">macro index .ab    &#34;&lt;pipe-entry&gt;/opt/homebrew/bin/lbdb-fetchaddr\n&#34;                # Store address details in lbdb.</span></span></code></pre></div>
<p>The value of mutt is less as my everything email client &ndash; I still keep up with mail from my phone or tablet &ndash; and more because over the years I&rsquo;ve tuned it to visualize and process mail from the keyboard. I still read mail with my phone or tablet every day, but don&rsquo;t like to do anything more than flag messages when I&rsquo;m out.</p>
<h3 id="html-mail-and-mutt">HTML mail and mutt</h3>
<p>mutt is sometimes hard to stick with because we lost the plaintext email battle. I know one designer who formats his email with monotype faces in what I&rsquo;d describe as a sort of problematic aesthetic revolt, but otherwise &hellip; we lost and we&rsquo;ll never be able to unsee Calibri.</p>
<p>Sticking <code>elinks</code>, <code>lynx</code> or <code>w3m</code> in your <code>~/.mailcap</code> to show HTML was best practice for a long while, provided you didn&rsquo;t mind also using <code>urlview</code> to display the links in a message once you&rsquo;d read it. I was involved in email marketing during the era when that approach worked, partially because to do HTML mail &ldquo;right&rdquo; back then meant you had someone doing it painstakingly by hand, then reviewing across a multitude of clients and platforms &ndash; you were heavily incentivized to keep it simple.  I bear the scars from a spat with a division of Siemens that was still using an old Lotus Notes and Win2000 in its marketing department. They were the customer, and the mail didn&rsquo;t look good to them, so it didn&rsquo;t look good. That was all, and we were forced to buy the next tier of service from our mail provider so we could add Lotus Notes on Win 2000 to our testing.</p>
<p>Anyhow, &ldquo;<code>elinks</code> in your mailcap and call it a day&rdquo; doesn&rsquo;t work anymore: image text, dozens of links, complex positional markup, etc. etc. etc. and it makes a lot of HTML mail a hash in a plaintext browser. There are a few ways to solve this on a Mac: One is punting to an actual browser, and the other is using the Mac Quicklook service:</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">text/html;  open -a firefox %s; nametemplate=%s.html text/html; open -a firefox %s; nametemplate=%s.html; copiousoutput
</span></span><span class="line"><span class="cl">text/html; qlmanage -p %s; nametemplate=%s.html</span></span></code></pre></div>
<p>The former does about what you&rsquo;d expect from looking at it: It uses Firefox (or whatever browser you&rsquo;d care to invoke) to open the HTML part of the message, at which point you can click links, etc.</p>
<p>The latter is a little more obscure, but basically recreates what happens when you select a file and tap the spacebar to preview it on a Mac, popping up a modal window with the contents. This often works well for other kinds of attachments. With HTML mail it is enough to let you read the mail but not really interact with it (e.g. follow a link). The <code>[DEBUG]</code> label at the front of the modal&rsquo;s title suggests that nobody at Apple really took the &ldquo;plain text zealots using this to browse HTML email&rdquo; use case into account when they wrote <code>qlmanage</code>, and the man page makes clear it&rsquo;s just a debugging tool. It&rsquo;ll do if you never intend to interact with a mail.</p>
<p>I&rsquo;m sticking to the Firefox approach. It&rsquo;s super fast if Firefox is already running (it always is), and you can follow links.</p>
<h3 id="these-things-leave-in-waves-too">These things leave in waves, too</h3>
<p>At some point mutt is going to do something to annoy me. I don&rsquo;t know what it&rsquo;s going to be this time. For now, I&rsquo;m just enjoying the periodic dustoff of my config and pleasures of zipping through my inbox. If I had to guess, it&rsquo;ll have something to do with getting email messages into org-mode or calendaring, somehow. There are a few shell-based CalDav clients out there, but they&rsquo;re very sync-and-cache oriented and I just don&rsquo;t like adding services. Had mutt never added IMAP caching and built-in SMTP support, I would have a harder time justifying it.</p>
<p>But I feel more protective of my Emacs and org-mode experience than anything. This run with Doom has yielded the most stable Emacs experience I&rsquo;ve had since switching to Macs mooostly full-time ca. 2004. No mystery segfaults. No hard-locking the UI because I have IMAP, RSS, IRC, and Twitter clients running in my text editor.</p>
]]></content:encoded>
    </item>
    <item>
      <title>mutt to org-contacts</title>
      <link>https://mike.puddingtime.org/posts/2023-04-15-mutt-to-org-contacts/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><author>mike@puddingtime.org (mike)</author>
      <guid>https://mike.puddingtime.org/posts/2023-04-15-mutt-to-org-contacts/</guid>
      <description>A little script to copy address information from mutt messages into an org-contacts file.</description>
      <content:encoded><![CDATA[<p>Part of making the whole <a href="https://mike.puddingtime.org/posts/20230413-making-a-plaintext-personal-crm-with-org-contacts/">plaintext CRM</a>  thing work involves capture: Getting contact information into the database. I made a capture template for <a href="https://beorgapp.com/">beorg</a> to make it easier to capture a contact on mobile, but I have a bunch of recent email contacts I&rsquo;ve wanted to add, too, and no easy way to get them out of mutt.</p>
<p>For all of its talents, mutt doesn&rsquo;t choose to expose any message variables to its macro functionality. If you want to extract a sender&rsquo;s name or email address or whatever, the message has to be piped. I put something like that together to <a href="https://mike.puddingtime.org/posts/2023-04-09-macros-to-score-mail-in-mutt/">add senders to a mutt score file</a>, and there are a few other things out there in the mutt ecosystem, like lbdb, that capture email addresses. lbdb could have been great for this, but its whole point is capture to a database of its own.</p>
<p>I&rsquo;ve been poking around with getting ChatGPT to write little utilities for me, so I put this problem to it, asking for a Ruby script that could pipe a message through formail to extract the needed name and email address.</p>
<p>It took a second prompt &ndash; initially it just wrote the code to process the input without specifying the input &ndash; but it produced something workable to get the two pieces of data out, and I added an org-contacts template as a <code>HERE</code> doc the script writes into a <code>mutt_contacts.org</code> file. I could have sent it straight to <code>contacts.org</code> but prefer to automate into buffer files to keep the chance of conflicts, sync or otherwise, to a minimum.</p>
<p>The accompanying mutt macro looks like this:</p>
<p><code>macro index,pager .oc &quot;|~/.mutt/org_contact.rb\n&quot;</code></p>
<p>I tap <code>.oc</code> when positioned on a message and it pipes into the script.</p>
<p>I&rsquo;d like to extend the macro to include a quick note, but for now it just adds the <code>NOTES</code> todo state and schedules the entry a day out to remind me to put something in there soon: The new entries turn up in my org-mode agenda for processing and re-filing into my <code>contacts.org</code> file.</p>
<p>And it worked well enough: Once I set it up I was able to run through and add nine or ten new contacts in a few seconds, then visit them in org-mode and refile them all.</p>
<p>And yeah &hellip;still holding the line against adding an MUA to Emacs proper. I just don&rsquo;t want to do the whole OfflineIMAP thing or similar, and I am still keeping things relatively simple. Adding <code>org-caldav</code> today was a small step in the wrong direction, but I like having calendar entries in my org agenda, it syncs relatively quickly once it does the initial download, and it has no system dependencies.</p>






<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="ch">#!/usr/bin/env ruby</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;open3&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;date&#39;</span>
</span></span><span class="line"><span class="cl"><span class="n">contacts_file</span> <span class="o">=</span> <span class="s2">&#34;~/org/mutt_contacts.org&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">message_contents</span> <span class="o">=</span> <span class="vg">$stdin</span><span class="o">.</span><span class="n">read</span>
</span></span><span class="line"><span class="cl"><span class="c1"># Define the command to extract headers with formail</span>
</span></span><span class="line"><span class="cl"><span class="n">command</span> <span class="o">=</span> <span class="s1">&#39;formail -X From: -X Sender: -X Reply-To: -x To: -x Cc: -x Bcc:&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Execute the command and capture the output</span>
</span></span><span class="line"><span class="cl"><span class="n">output</span><span class="p">,</span> <span class="n">status</span> <span class="o">=</span> <span class="no">Open3</span><span class="o">.</span><span class="n">capture2</span><span class="p">(</span><span class="n">command</span><span class="p">,</span> <span class="ss">stdin_data</span><span class="p">:</span> <span class="n">message_contents</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="n">status</span><span class="o">.</span><span class="n">success?</span>
</span></span><span class="line"><span class="cl">  <span class="n">email</span> <span class="o">=</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl">  <span class="nb">name</span> <span class="o">=</span> <span class="kp">nil</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Parse the output and extract the email and name from the From, Sender, and Reply-To headers</span>
</span></span><span class="line"><span class="cl">  <span class="n">output</span><span class="o">.</span><span class="n">lines</span><span class="o">.</span><span class="n">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">line</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">    <span class="k">case</span> <span class="n">line</span>
</span></span><span class="line"><span class="cl">    <span class="k">when</span> <span class="sr">/^From:\s*(.*)$/i</span><span class="p">,</span> <span class="sr">/^Sender:\s*(.*)$/i</span><span class="p">,</span> <span class="sr">/^Reply-To:\s*(.*)$/i</span>
</span></span><span class="line"><span class="cl">      <span class="n">from</span> <span class="o">=</span> <span class="vg">$1</span><span class="o">.</span><span class="n">strip</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1"># Extract the email and name from the From, Sender, or Reply-To header</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="n">from</span> <span class="o">=~</span> <span class="sr">/(.+?)\s*&lt;(.+?)&gt;/</span>
</span></span><span class="line"><span class="cl">        <span class="n">email</span> <span class="o">=</span> <span class="vg">$2</span>
</span></span><span class="line"><span class="cl">        <span class="nb">name</span> <span class="o">=</span> <span class="vg">$1</span><span class="o">.</span><span class="n">strip</span>
</span></span><span class="line"><span class="cl">      <span class="k">else</span>
</span></span><span class="line"><span class="cl">        <span class="n">email</span> <span class="o">=</span> <span class="n">from</span>
</span></span><span class="line"><span class="cl">        <span class="nb">name</span> <span class="o">=</span> <span class="s2">&#34;No Name Found&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1"># Found the email and name, so break the loop</span>
</span></span><span class="line"><span class="cl">      <span class="k">break</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">org_template</span> <span class="o">=</span> <span class="o">&lt;&lt;~</span><span class="no">TEMPLATE</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="o">**</span> <span class="no">NOTES</span> <span class="c1">#{name} :mutt:</span>
</span></span><span class="line"><span class="cl">  <span class="ss">SCHEDULED</span><span class="p">:</span> <span class="c1">#{(Date.today + 1).strftime(&#34;%Y-%m-%d&#34;)}</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:PROPERTIES</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:EMAIL</span><span class="p">:</span> <span class="c1">#{email}</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:PHONE</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:BIRTHDAY</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:CONTACTED</span><span class="p">:</span> <span class="c1">#{Date.today.strftime(&#34;%Y-%m-%d&#34;)}</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:END</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">TEMPLATE</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">File</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="n">contacts_file</span><span class="p">,</span> <span class="s2">&#34;a&#34;</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">f</span><span class="o">|</span> <span class="n">f</span><span class="o">.</span><span class="n">write</span> <span class="n">org_template</span><span class="p">}</span></span></span></code></pre></div>
]]></content:encoded>
    </item>
  </channel>
</rss>
