Dodo - A graphical, hackable email client based on notmuch

Overview

Dodo

Documentation Status

Dodo is a graphical email client written in Python/PyQt5, based on the command line email swiss-army-knife notmuch. It's main goals are to:

  • offer efficient, keyboard-oriented mail reading, sorting, and composing
  • give a mostly text-based email experience by default, but with HTML support a few keystrokes away
  • offload as much work as possible on existing, excellent command-line tools (UNIX philosphy-style)
  • be simple enough to customise and hack on yourself

This README has instructions on installation, usage, and basic configuration. For API documentation (which is also useful for configuration), check out the Read the Docs page.

As an email client, Dodo is pretty much feature-complete, but not yet extensively tested. Since it's based on notmuch, all of its features are non-destructive, so you shouldn't ever lose any email due to bugs. That being said, you might see some strange behaviour, so use at your own risk.

A lot of Dodo's design is inspired by two existing notmuch-based clients: alot and astroid.

Prerequisites

If you have already used notmuch for email, there's not much to do here :). If not, you'll need to set up some other programs first:

  • something to check mail and sync with a local Maildir (offlineimap is the default, but others like mbsync should work fine)
  • a sendmail-compatible SMTP client to send mail (msmtp is the default)
  • notmuch for email searching and tagging
  • w3m for translating HTML messages into plaintext

All of this is pretty standard stuff, and should be installable via your package manager on Linux/Mac/etc. If you don't know how to set these things up already, see the respective websites or the "Setting up the prerequisites" section below for a quick reference.

Install and run

Make sure you have Python 3.7+ and PyQt5. Clone Dodo with:

% git clone https://github.com/akissinger/dodo.git

Then, add the bin/ subdirectory to your PATH and run with:

% dodo

An optional Python dependency is lxml for some limited HTML sanitization, which is off by default (see the next section for switching it on).

Configuration

Dodo is configured via ~/.config/dodo/config.py. This is just a Python file that gets eval-ed right before the main window is shown.

Most settings have reasonable defaults (assuming your are using offlineimap/msmtp), which can be found in settings.py. The only two things that must be set for Dodo to work properly are your email address and the location of your sent mail folder. Some things you probably also want to set up are the text editor (for composing messages) and the file browser (for viewing attachments).

Here is an example config.py, with some settings similar to the ones I use:

import dodo

# required
dodo.settings.email_address = 'First Last <[email protected]>'
dodo.settings.sent_dir = '/home/user/mail/Work/Sent'

# optional
dodo.settings.theme = dodo.themes.nord
dodo.settings.editor_command = ['kitty', 'nvim', '-c', 'set tw=0']
dodo.settings.file_browser_command = ['fman', '/home/user/Documents/']

A theme is just a Python dictionary mapping some fixed color names to HTML color codes. Currently, the themes implemented in themes.py are nord, solarized_light and solarized_dark. If you want more, feel free to roll your own, or (better) send me a pull request!

All of the settings of the form ..._command are given as a list consisting of the command and its arguments. Additional arguments, such as the relevant folder or file are appended to this list.

The settings above replace the default text editor (xterm -e vim) with neovim run inside a new kitty terminal. I am also using Michael Herrmann's excellent dual-pane file manager fman instead of the default (nautilus). With these settings, showing attachments will open fman with a fixed directory in the left pane (/home/user/Documents) and a directory containing the attachments on the right. A similar effect can be obtained with ranger using the multipane view mode.

While Javascript is disabled in the HTML email viewer, you may want to set up a custom HTML sanitizer function as follows:

dodo.util.html2html = dodo.util.clean_html2html

The above function passes the HTML through the Cleaner object of the lxml library. Note this still allows some dodgy stuff, such as calling home via embedded img tags. Fully safe and private HTML email from untrusted sources should be considered a work-in-progress.

Key mapping

Key mappings can be customised by changing the dictionaries defined in keymap.py. These map a key to a pair consisting of a description string and a Python function. For the global_keymap, this function takes the Dodo object defined in app.py as its argument. The other maps take the relevant "local" widget (SearchView, ThreadView, ComposeView, or CommandBar).

To bind a single key, you can write something like this in config.py:

dodo.keymap.search_keymap['t'] = (
  'toggle todo',
  lambda p: p.toggle_thread_tag('todo'))

or you can replace the keymap completely from config.py, e.g.:

dodo.keymap.search_keymap = {
  'C-n': ('next thread', lambda p: p.next_thread()),
  'C-p': ('previous thread', lambda p: p.previous_thread()),
  # ...
}

The keymaps used by Dodo are global_keymap, search_keymap, thread_keymap, and command_bar_keymap. All the keymaps except command_bar_keymap also support keychords, which are represented as space-separated sequences of keypresses, e.g.

dodo.keymap.global_keymap['C-x C-c'] = (
  'exit emacs ... erm, I mean Dodo',
  lambda a: a.quit())

You can unmap a single key by deleting it from the dictionary:

del dodo.keymap.global_keymap["Q"]

Basic use

Most functionality in Dodo comes from keyboard shortcuts. Press ? to get a full list of the key mappings at any time.

Dodo has 3 different kinds of view: search views, thread views, and compose views. It opens initially with a search view with the query tag:inbox. Pressing enter or double-clicking a thread with open that thread in the thread view. Pressing c at any time or r while looking at a message in the thread view will open the compose view.

In the compose view, press <enter> to edit the message on your chosen editor. Once you save and exit, the message will be updated. Press a to add attachments (or use the special A: header). Press S to send.

Setting up the prerequisites

Since there's a lot of little bits to configure, I've also included some minimal configurations for offlineimap, msmtp, and notmuch, just to have it all in one place.

Note the offlineimap and msmtp configurations below simply read the password from a plaintext file. More secure options are available, which are explained in the respective docs.

Incoming mail

Assuming your system configuration directory is ~/.config/, the configuration file for offlineimap is located in ~/.config/offlineimap/config. Here is a template for syncing one IMAP account named "Work":

[general]
accounts = Work

[Account Work]
localrepository = WorkLocal
remoterepository = WorkRemote

[Repository WorkLocal]
type = Maildir
localfolders = ~/mail/Work

[Repository WorkRemote]
type = IMAP
remotehost = (IMAP SERVER)
remoteuser = (USER NAME)
remotepassfile = (PASSWORD FILE)
sslcacertfile = OS-DEFAULT

If you want to set up multiple IMAP accounts, just put them all in the ~/mail folder and set ~/mail as your database path for notmuch.

Outgoing mail

Here is a sample ~/.config/msmtp/config, setting up a single SMTP server (also named "Work") with TLS:

defaults
auth           on
tls            on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile        ~/.msmtp.log
account        Work
host           (SMTP SERVER)
port           587
from           (EMAIL ADRESS)
user           (USER NAME)
passwordeval   cat (PASSWORD FILE)
account        default : Work

You may need to change the 4th line if your system stores CA certificates in a different location.

Mail indexing

Once offlineimap is set up, just run notmuch from the command line to do some initial setup, which gets saved in ~/.notmuch-config by default. You can set ~/mail as your database path. notmuch has lots of options, the ability to set up various hooks and filters, and to sync certain IMAP markers with notmuch tags.

Here's a ~/.notmuch-config which is roughly like the one I use:

[database]
path=/home/user/mail

[user]
name=First Last
[email protected]

[new]
tags=new
ignore=

[search]
exclude_tags=deleted;killed;spam;

[maildir]
synchronize_flags=true
Comments
  • Configuring vim in xfce4-terminal as editor

    Configuring vim in xfce4-terminal as editor

    Depending on the terminal specification, the current definition scheme for the editor can fail. I'm trying to use the following in my config file:

    dodo.settings.editor_command = ["xfce4-terminal", "--disable-server", "--geometry", "86x24+40+60", "-e", "vim"]
    

    and I get an error xfce4-terminal: Unknown option "/tmp/tmp9ztrlszu.eml". In qutebrowser, for example, I can add {file} after "vim" and thus the call works.

    opened by agenbite 3
  • Problem with mbsync

    Problem with mbsync

    I've set dodo.settings.sync_mail_command = 'mbsync my-mail-box' and I'm getting this error at regular intervals:

    FileNotFoundError: [Errno 2] No such file or directory: 'mbsync my-mail-box'
    

    However, I have a system service for fetching mail through IMAP IDLE and I don't need Dodo to take care of fetching mail. Thus, I've set sync_mail_command to '' and the error is now

    PermissionError: [Errno 13] Permission denied: ''
    
    opened by agenbite 2
  • FR: change font in config file

    FR: change font in config file

    Another thing that would be nice is to be able to define a font which can be used globally: currently, by setting dodo.settings.search_font we don't affect the css.

    opened by agenbite 2
  • FR: Forward message

    FR: Forward message

    Congrats on this interesting software which might indeed fill a gap. I gave it a try and missed the forwarding messages functionality. It'd be nice to have the possibility to forward an html-only message as a plain text message, something that I've found difficult in notmuch-emacs.

    Keep it up!!

    opened by agenbite 2
  • Add DesktopFilename for app such that it get recognized by the window…

    Add DesktopFilename for app such that it get recognized by the window…

    Add DesktopFilename to the application (Very small PR). It seems that window managers use this to set the name/app_id (at least here in wayland/sway).

    opened by cinghiopinghio 1
  • [feature request] reload config.

    [feature request] reload config.

    It would be cool to be able to reload the configuration (especially in an automated way or with a remote command), in particular this is my use case:

    I managed to start dodo with a theme that match the current wm theme. At some point i want to change to dark or light theme. As of now I need to first change theme and then close and reopen dodo to make it follow the changes.

    In astroid there was not such issue since, using gtk, it was automagically following the gtk theme.

    opened by cinghiopinghio 1
  • [feature request] multiple accounts

    [feature request] multiple accounts

    Thanks for this sleek MUA. Nowadays, many of us use multiple email accounts for different channel of communication. Is it a planned feature to support multiple accounts? As of now, it seams only one account is configurable.

    opened by cinghiopinghio 1
  • Use notmuch's python bindings

    Use notmuch's python bindings

    While investigating how to change the separator of the authors list in the search view (which is | [bar+space] and should be , [comma+space] or something similar), I noticed that Dodo makes calls to notmuch through subprocess.

    Wouldn't it be better to use notmuch's python bindings instead of calls to subprocess.run?

    opened by agenbite 1
  • Define custom bindings in config file

    Define custom bindings in config file

    It is somehow inconvenient to customize key bindings in the source code tree, since any git pull will complain of conflicting changes and force the user to stash/merge. Wouldn't it be nicer to edit the bindings directly in the config.py file? I tried to copy the mapping dictionaries into that file and it doesn't seem to work.

    (Sorry for too many feature requests and too few PRs, but python is not my mother tongue... Thanks again for this software!!)

    opened by agenbite 1
  • FR: Use ranger to pick attachments

    FR: Use ranger to pick attachments

    Ranger's option --choosefiles could allow the user to pick the attachments without using the mouse. Is it possible to configure the file picker in the same way as the attachment viewer is configured?

    opened by agenbite 1
  • Caching message counts in tag view

    Caching message counts in tag view

    This is the notmuch client I’ve been looking for!

    I’ve noticed that with a very large (100k or more messages) database of archived email, re-querying message counts when the tag view is loaded has a significant delay. Might be nice to cache message counts and/or make the tallying a user-initiated process.

    opened by evanheller 0
  • [feature request] dodo over ssh

    [feature request] dodo over ssh

    Hello,

    I am used to the following notmuch setup: all my emails are stored on a remote server and I use neomutt to access it throught ssh. I have also a local notmuch wrapper script to allow for some local operations (as described somewhere on the notmuch pages).

    As far I see, this setup is mostly transparent for dodo since it seems to simply use whatever notmuch executable which is found in the PATH. However, this obviously does not work for attachments and for any operation needing to access the raw file. Can I do something in my config.py in the current state of dodo ? And if not, what would be the best way to deal with it ?

    I imagine two different paths:

    • mount the remote maildir hierarchy over sshfs (but it needs to deal with the absolute path used in the xapian index)
    • retrieve each indivudal file when needed with scp

    In both cases, we need some possibility to overload the Python code used to open files and I do not seen an obvious way to do that cleanly.

    What do you think ?

    opened by oschwand 0
  • Scroll the help window

    Scroll the help window

    Glad to see this keeps growing! :)

    I find myself hitting 'j' in order to browse the help window containing the bindings. Would it be possible to customize bindings in this window too?

    opened by agenbite 0
Owner
Aleks Kissinger
Aleks Kissinger
Dns-Client-Server - Dns Client Server For Python

Dns-client-server DNS Server: supporting all types of queries and replies. Shoul

Nishant Badgujar 1 Feb 15, 2022
Raphtory-client - The python client for the Raphtory project

Raphtory Client This is the python client for the Raphtory project Install via p

Raphtory 5 Apr 28, 2022
OSINT tool to get information from a Github and Gitlab profile and find user's email addresses leaked on commits.

gitrecon OSINT tool to get information from a Github or Gitlab profile and find user's email addresses leaked on commits. ?? How does this work? GitHu

GOΠZO 211 Dec 17, 2022
Get Notified about vaccine availability in your location on email & sms ✉️! Vaccinator Octocat tracks & sends personalised vaccine info everday. Go get your shot ! 💉

Vaccinater Get Notified about vaccine availability in your location on email & sms ✉️ ! Vaccinator Octocat tracks & sends personalised vaccine info ev

Mayukh Pankaj 6 Apr 28, 2022
Short Program using Transavia's API to notify via email an user waiting for a flight at special dates and with the best price

Flight-Notifier Short Program using Transavia's API to notify via email an user waiting for a flight at special dates and with the best price Algorith

Wassim 2 Apr 10, 2022
This bot will send you an email or notify you via telegram & discord if dolar/lira parity breaks a record.

Dolar Rekor Kırdı Mı? This bot will send you an email or notify you via Telegram & Discord if Dolar/Lira parity breaks a record. Mailgun can be used a

Yiğit Göktuğ Budanur 2 Oct 14, 2021
A simple Facebook Account generator, written in python (needs different Email so Accounts do not get banned)

FacebookAccountGenerator FAB is a Facebook-Account generating script, written in python Installation Use the package manager pip to install selenium p

MrOverload 7 Jan 5, 2023
A project that automatically sends you a Medium article on a topic of your choosing to your email address daily.

Daily Article from Medium ✏️ About A project that automatically sends you a Medium article on a topic of your choosing to your email address daily. No

Orhan Emre Dikicigil 2 Apr 27, 2022
AWS Lambda - Parsing Cloudwatch Data and sending the response via email.

AWS Lambda - Parsing Cloudwatch Data and sending the response via email. Author: Evan Erickson Language: Python Backend: AWS / Serverless / AWS Lambda

Evan Scott Erickson 1 Nov 14, 2021
Signs the target email up to over 1000 different mailing lists to get spammed each day.

Email Bomber Say goodbye to that email Features Signs up to over 1k different mailing lists Written in python so the program is lightweight Easy to us

Loxdr 1 Nov 30, 2021
Periodically check the manuscript state in the scholar one system and send email when finding a new state.

ScholarOne-manuscript-checker Periodically check the manuscript state in the scholar one system and send email when finding a new state. Parameters ne

null 2 Aug 18, 2022
Red-mail - Advanced email sending library for Python

Red Mail Next generation email sender What is it? Red Mail is an advanced email

Mikael Koli 313 Jan 8, 2023
Send SMS text messages via email with as many accounts as you want :)

SMS-Spammer Send SMS text messages via email with as many accounts as you want :) Example Set Up Guide! To start log into the gmail account you would

Riceblades11 10 Oct 25, 2022
Discord-Token-Formatter - A simple script to convert discord tokens from email token to token only format

Discord-Token-Formatter A simple script to convert discord tokens from email:pas

null 2 Oct 23, 2022
An Advance Discord Generator Written in python Verified Email and Phone Number For Free!

Intro An Advance Discord Generator Written in python It can generate nearly fully verified tokens USAGE put server invite code inside ( invitecode = "

null 36 May 2, 2022
A tiktok mass account creator with undetected selenium and email verification, to bot an account

⚠️ STILL UNDER DEVELOPEMENT - v1.1-beta ⚠️ Adding PROXY ROTATION Adding EMAIL VERIFICATION Adding USERNAME COMPILER Tiktok Mass Bot Creator v1.1-beta

xtekky 11 Aug 1, 2022
🐍 The official Python client library for Google's discovery based APIs.

Google API Client This is the Python client library for Google's discovery based APIs. To get started, please see the docs folder. These client librar

Google APIs 6.2k Jan 8, 2023
python3.5+ hubspot client based on hapipy, but modified to use the newer endpoints and non-legacy python

A python wrapper around HubSpot's APIs, for python 3.5+. Built initially around hapipy, but heavily modified. Check out the documentation here! (thank

Jacobi Petrucciani 140 Dec 21, 2022