API reference

dodo.app

dodo.commandbar

dodo.compose

dodo.helpwindow

dodo.keymap

command_bar_keymap = {'<down>': ('history next', <function <lambda>>), '<enter>': ('accept', <function <lambda>>), '<escape>': ('close', <function <lambda>>), '<up>': ('history previous', <function <lambda>>), 'C-n': ('history next', <function <lambda>>), 'C-p': ('history previous', <function <lambda>>)}

The keymap active when the command bar is visible

A dictionary from key strings to pairs consisting of a short docstring and a function taking CommandBar as input. Unlike the other keymaps, the command bar keymap doesn’t accept keychords. Also, you should avoid mapping alphanumeric keys to commands, as this will interfere with typing.

compose_keymap = {'<enter>': ('edit message', <function <lambda>>), 'S': ('send', <function <lambda>>), '[': ('previous SMTP account', <function <lambda>>), ']': ('next SMTP account', <function <lambda>>), 'a': ('attach file', <function <lambda>>), 'e': ('toggle PGP-encrypt', <function <lambda>>), 's': ('toggle PGP-sign', <function <lambda>>), 'w': ('toggle word wrap', <function <lambda>>)}

The local keymap for compose panels

A dictionary from key strings to pairs consisting of a short docstring and a function taking ComposePanel as input.

global_keymap = {'/': ('search', <function <lambda>>), '?': ('show help', <function <lambda>>), 'F': ('show flagged', <function <lambda>>), 'I': ('show inbox', <function <lambda>>), 'Q': ('quit', <function <lambda>>), 'T': ('show tags', <function <lambda>>), 'U': ('show unread', <function <lambda>>), 'X': ('close all', <function <lambda>>), '`': ('sync mail', <function <lambda>>), 'c': ('compose', <function <lambda>>), 'h': ('previous panel', <function <lambda>>), 'l': ('next panel', <function <lambda>>), 't m': ('tag marked', <function <lambda>>), 't t': ('tag', <function <lambda>>), 'x': ('close panel', <function <lambda>>)}

The global keymap

A dictionary from key strings to pairs consisting of a short docstring and a function taking Dodo as input. This commands can be superseded by the various local keymaps.

search_keymap = {'<down>': ('next thread', <function <lambda>>), '<enter>': ('open thread', <function <lambda>>), '<pagedown>': ('page down', <function <lambda>>), '<pageup>': ('page up', <function <lambda>>), '<space>': ('toggle marked', <function <lambda>>), '<tab>': ('next unread', <function <lambda>>), '<up>': ('previous thread', <function <lambda>>), 'C-d': ('down 20', <function <lambda>>), 'C-u': ('up 20', <function <lambda>>), 'G': ('last thread', <function <lambda>>), 'S-<tab>': ('previous unread', <function <lambda>>), 'a': ('tag -inbox -unread', <function <lambda>>), 'f': ('toggle flagged', <function <lambda>>), 'g g': ('first thread', <function <lambda>>), 'j': ('next thread', <function <lambda>>), 'k': ('previous thread', <function <lambda>>), 'u': ('toggle unread', <function <lambda>>)}

The local keymap for search panels

A dictionary from key strings to pairs consisting of a short docstring and a function taking SearchPanel as input.

tag_keymap = {'<down>': ('next tag', <function <lambda>>), '<enter>': ('search tag', <function <lambda>>), '<up>': ('previous tag', <function <lambda>>), 'C-d': ('down 20', <function <lambda>>), 'C-u': ('up 20', <function <lambda>>), 'G': ('last tag', <function <lambda>>), 'g g': ('first tag', <function <lambda>>), 'j': ('next tag', <function <lambda>>), 'k': ('previous tag', <function <lambda>>)}

The local keymap for the tag panel

A dictionary from key strings to pairs consisting of a short docstring and a function taking TagPanel as input.

thread_keymap = {'-': ('page up', <function <lambda>>), '<pagedown>': ('page down', <function <lambda>>), '<pageup>': ('page up', <function <lambda>>), '<space>': ('page down', <function <lambda>>), 'A': ('show attachments in file browser', <function <lambda>>), 'C-d': ('scroll down more', <function <lambda>>), 'C-f': ('forward', <function <lambda>>), 'C-u': ('scroll up more', <function <lambda>>), 'G': ('bottom of message', <function <lambda>>), 'H': ('toggle HTML', <function <lambda>>), 'J': ('next message', <function <lambda>>), 'K': ('previous message', <function <lambda>>), 'M': ('toggle thread list mode', <function <lambda>>), 'R': ('reply', <function <lambda>>), 'U': ('next matching unread message', <function <lambda>>), 'f': ('toggle flagged', <function <lambda>>), 'g g': ('top of message', <function <lambda>>), 'j': ('scroll down', <function <lambda>>), 'k': ('scroll up', <function <lambda>>), 'r': ('reply to all', <function <lambda>>), 'u': ('toggle unread', <function <lambda>>)}

The local keymap for thread panels

A dictionary from key strings to pairs consisting of a short docstring and a function taking ThreadPanel as input.

dodo.panel

dodo.settings

This module holds settings and sets their default values. The values set here should be overridden by the user in ~/.config/dodo/config.py. This can be done as follows:

import dodo
dodo.settings.email_address = 'First Last <me@domain.com>''
dodo.settings.sent_dir = '~/mail/work/Sent'

The settings email_address and sent_dir are required. Dodo may not work correctly unless you set them properly. The rest of the settings have reasonable defaults, as detailed below.

default_thread_list_mode: Literal['conversation', 'thread'] = 'conversation'

Set the way your thread should be listed.

Possible values are:
  • ‘conversation’: flat list, chronologically sorted

  • ‘thread’: tree view, following the various subthreads

default_to_html = False

Open messages in HTML mode by default, rather than plaintext

editor_command = "xterm -e vim '{file}'"

Command used to launch external text editor

This is a shell command, which additionally takes the {file} placeholder, which is passed the name of a temp file being edited while composing an email.

email_address: str | Dict[str, str] = ''

Your email address (REQUIRED)

This is used both to populate the ‘From’ field of emails and to (mostly) avoid CC’ing yourself when replying to all. It can be given as ‘NAME <ADDRESS@DOMAIN>’ format. For just one email address, this can be given as a string. From multiple emails, use a dictionary mapping the account names in smtp_accounts to the associated email addresses.

file_browser_command = "nautilus '{dir}'"

Command used to launch external file browser

This is a shell command, which additionally takes the {dir} placeholder. This command is used when viewing attachments, which first dumps the attachments to a temp directory given by {dir}, then opens that directory in a file browser.

file_picker_command = None

Command used to launch external file picker

This is an optional shell command, which additionally takes the {tempfile} placeholder. This command is used when picking files to attach to an email. The command should write out the chosen files to {tempfile}, which will then be read and deleted, if it exists.

By default, this is set to None, in which case the built-in file picker will be used.

gnupg_home = None

Directory containg GnuPG keys

If set to None, GnuPG will use whatever directory is the default (consult the GnuPG documentation for more information on what this might be).

gnupg_keyid = None

The id of the key to be used for GnuPG-signing mail messages.

If set to the id of a valid GnuPG private signing key, sent messages will be cryptographically signed according to rfc3156 using the GnuPG sotware, which should be installed and configured. Requires python-gnupg (https://pypi.org/project/python-gnupg/)

hide_tags = ['unread', 'sent']

Tags to hide in search panel

html_block_remote_requests = True

Block remote requests for HTML messages

HTML messages, especially from dodgy senders, can display remote content or ‘call home’ from embedded image tags or iframes. If set to True, Dodo will not allow these requests.

Display a confirmation dialog before opening a link in browser

If this is True, Dodo will display a confirmation dialog showing the actual URL that the web browser will request before opening. This is an extra measure against phishing or emails opening your web browser without your permission.

A list of trusted hosts for HTML links.

If a link is to a host in this list, it will be opened without confirmation, even if html_confirm_open_links is True.

init_queries = ['tag:inbox']

List of non closable queries open at startup

You can save query with notmuch config set query:inbox “tag:inbox and not tag:trash” and use query:inbox as a search term.

message2html_filters = []

A list of functions to extract text from a mail message JSON.

Every item in this list should be a function, which either returns a HTML string (which gets formatted inside a <pre> tag), or returns None. The first function to return a non-None value is used to render the message. If all functions return None, the default rendering is used.

The default rendering runs the following functions in order, which might also be useful when writing your own filters:

Example configuration using this feature to highlight markdown syntax:

import pygments.formatters
from dodo import util

def render_github(msg):
    # Double imports needed due to how dodo runs config.py
    import pygments.lexers
    import pygments.formatters

    # If you use some sort of auto-tagging, you might want to match on
    # tags instead of headers.
    if "headers" not in msg or "From" not in msg["headers"]:
        return None
    if not msg["headers"]["From"].endswith("<notifications@github.com>"):
        return None

    text = util.body_text(msg)
    lexer = pygments.lexers.MarkdownLexer()
    formatter = pygments.formatters.HtmlFormatter(nowrap=True)
    highlighted = pygments.highlight(text, lexer, formatter)
    return util.linkify(highlighted)

dodo.settings.message2html_filters = [render_github]

# Available styles: https://pygments.org/styles/
pygments_css = pygments.formatters.HtmlFormatter(style="gruvbox-dark").get_style_defs()
dodo.settings.message_css += pygments_css.replace("{", "{{").replace("}", "}}")
message_css = '\npre {{\n  font-family: {message_font};\n  font-size: {message_font_size}pt;\n}}\n\npre .quoted {{\n  color: {fg_dim};\n}}\n\npre .headername {{\n  color: {fg_bright};\n  font-weight: bold;\n}}\n\npre .headertext {{\n  color: {fg_bright};\n}}\n\nbody {{\n  background-color: {bg};\n  color: {fg};\n}}\n\n::-webkit-scrollbar {{\n  background: {bg};\n}}\n\n::-webkit-scrollbar-thumb {{\n  background: {bg_button};\n}}\n\n::selection {{\n  color: {bg};\n  background: {fg};\n}}\n\na {{\n  color: {fg_bright};\n}}\n'

CSS used in view and compose window

Placeholders may be included in curly brackets for any color named in the current theme, as well as {message_font} and {message_font_size}. Literal curly braces should be doubled, i.e. ‘{’ should be ‘{{’ and ‘}’ should be ‘}}’.

message_font = 'DejaVu Sans Mono'

The font used for plaintext messages

message_font_size = 12

The font size used for plaintext messages

no_hooks_on_send = True

disable/enable calling notmuch hooks when sending email

When True, ‘notmuch new’ is called with –no-hooks when a message is sent. One may not wanting to wait for the hooks on each sent email, for example when calling mbsync on their notmuch hooks. Other users may set this to False, for example when notmuch hooks are used to archive sent mail.

remove_temp_dirs = 'ask'

Set whether to remove temporary directories when closing a panel

Thread panels create temporary directories to open attachments. These can be cleaned up automatically when a panel (or Dodo) is closed. Possible values are: ‘always’, ‘never’, or ‘ask’.

search_color_overrides = {}

A dictionary mapping tags to color dictionaries.

The color dictionaries map columns to override colors. The available columns are:

  • date

  • from

  • subject

  • tags

For example, to show a red subject for messages tagged ‘urgent’, using the built-in Gruvbox palette:

dodo.settings.search_color_overrides = {
    'urgent': {
        'subject': dodo.themes.gruvbox_p['neutral_red'],
    }
}
search_font = 'DejaVu Sans Mono'

The font used for search output and various other list-boxes

search_font_size = 13

The font size used for search output and various other list-boxes

search_title_format = '{query} [{num_threads}]'

A Python format string for the tab title of search panels

The following placeholders can be used:

  • {query}: the current search query

  • {num_threads}: the number of threads returned by the search

search_view_padding = 1

A bit of spacing around each line in the search panel

send_mail_command: str | dict[str, str] = 'msmtp -a "{account}" -t'

Command used to send mail via SMTP

Either a plain command or a mapping of account names to command.

The command must be a shell command that expects a (sendmail-compatible) email message to be written to STDIN. Note that it should read the destination from the From: header of the message and not a command-line argument. Use the {account} placeholder to read the currently selected account.

sent_dir = ''

Where to store sent messages (REQUIRED)

This will usually be a subdirectory of the Maildir sync’ed with sync_mail_command. This setting can be given either as a string to use one global sent directory, or as a dictionary mapping account names in smtp_accounts to their own sent dirs.

A value of None, either standalone or as one of the dict value, can be used to indicate the email should be discarded. This can be useful if the sendmail command already has a mechanism for that feature.

smtp_accounts = ['default']

A list of SMTP account names recognised by send_mail_command

This setting allows switching SMTP accounts in the Compose panel. The first account in the list is selected by default.

sync_mail_command = 'offlineimap'

Command used to sync IMAP with local Maildir

sync_mail_interval = 300

Interval to run sync_mail_command automatically, in seconds

Set this to -1 to disable automatic syncing.

tag_font = 'DejaVu Sans Mono'

The font used for tags and tag icons

tag_font_size = 13

The font size used for tags and tag icons

tag_icons = {'attachment': '\uf565', 'flagged': '\uf73a', 'inbox': '\uf01c', 'marked': '\uf111', 'replied': '\uf4a8', 'sent': '>', 'signed': '\uf040', 'unread': '\uf0e0'}

Tag icons

This is a dictionary of substitutions used to abbreviate common tag names as unicode icons in the search and thread panels.

theme = {'bg': '#2e3440', 'bg_alt': '#3b4252', 'bg_button': '#4c566a', 'bg_highlight': '#a3be8c', 'fg': '#d8dee9', 'fg_bad': '#bf616a', 'fg_bright': '#b48ead', 'fg_button': '#eceff4', 'fg_date': '#4c566a', 'fg_dim': '#4c566a', 'fg_from': '#5e81ac', 'fg_good': '#a3be8c', 'fg_highlight': '#2e3440', 'fg_link': '#81a1c1', 'fg_subject': '#d8dee9', 'fg_subject_flagged': '#ebcb8b', 'fg_subject_irrelevant': '#4c566a', 'fg_subject_unread': '#b48ead', 'fg_tags': '#81a1c1'}

The GUI theme

A theme is a dictionary mapping a dozen or so named colors to HEX values. Several themes are defined in dodo.themes, based on the popular Nord, Solarized and Gruvbox color palettes.

web_browser_command = ''

Web browser to use when clicking links in emails

This should be a single command which expects a URL as its first argument. If this is an empty string, Dodo will attempt to use the default web browser supplied by the desktop environment, if it exists.

wrap_column = 78

Wrap text to this column when composing emails

wrap_message = True

Hard-wrap message text by default

You may wish to disable this if you don’t want hard wraps in your email messages or your text editor does hard wrapping already.

dodo.themes

apply_theme(theme)

“Apply the given theme to GUI components

This is called when Dodo is initialised.

Return type:

None

catppuccin_macchiato = {'bg': '#24273a', 'bg_alt': '#181926', 'bg_button': '#363a4f', 'bg_highlight': '#8aadf4', 'fg': '#cad3f5', 'fg_bad': '#ed8796', 'fg_bright': '#b7bdf8', 'fg_button': '#f4dbd6', 'fg_date': '#f0c6c6', 'fg_dim': '#8087a2', 'fg_from': '#8aadf4', 'fg_good': '#a6da95', 'fg_highlight': '#181926', 'fg_link': '#8aadf4', 'fg_subject': '#cad3f5', 'fg_subject_flagged': '#eed49f', 'fg_subject_irrelevant': '#939ab7', 'fg_subject_unread': '#c6a0f6', 'fg_tags': '#f5a97f'}

Theme based on the Catppuchin palette (macchiatto version).

gruvbox_dark = {'bg': '#282828', 'bg_alt': '#3c3836', 'bg_button': '#3c3836', 'bg_highlight': '#282828', 'fg': '#ebdbb2', 'fg_bad': '#cc241d', 'fg_bright': '#fbf1c7', 'fg_button': '#d5c4a1', 'fg_date': '#689d6a', 'fg_dim': '#a89984', 'fg_from': '#458588', 'fg_good': '#98971a', 'fg_highlight': '#d79921', 'fg_link': '#b16286', 'fg_subject': '#bdae93', 'fg_subject_flagged': '#d65d0e', 'fg_subject_irrelevant': '#928374', 'fg_subject_unread': '#98971a', 'fg_tags': '#b16286'}

Theme based on the Gruvbox palette (dark background)

gruvbox_dark_hard = {'bg': '#1d2021', 'bg_alt': '#3c3836', 'bg_button': '#3c3836', 'bg_highlight': '#282828', 'fg': '#ebdbb2', 'fg_bad': '#cc241d', 'fg_bright': '#fbf1c7', 'fg_button': '#d5c4a1', 'fg_date': '#689d6a', 'fg_dim': '#a89984', 'fg_from': '#458588', 'fg_good': '#98971a', 'fg_highlight': '#d79921', 'fg_link': '#b16286', 'fg_subject': '#bdae93', 'fg_subject_flagged': '#d65d0e', 'fg_subject_irrelevant': '#928374', 'fg_subject_unread': '#98971a', 'fg_tags': '#b16286'}

Theme based on the Gruvbox palette (dark background, hard contrast)

gruvbox_dark_soft = {'bg': '#32302f', 'bg_alt': '#3c3836', 'bg_button': '#3c3836', 'bg_highlight': '#282828', 'fg': '#ebdbb2', 'fg_bad': '#cc241d', 'fg_bright': '#fbf1c7', 'fg_button': '#d5c4a1', 'fg_date': '#689d6a', 'fg_dim': '#a89984', 'fg_from': '#458588', 'fg_good': '#98971a', 'fg_highlight': '#d79921', 'fg_link': '#b16286', 'fg_subject': '#bdae93', 'fg_subject_flagged': '#d65d0e', 'fg_subject_irrelevant': '#928374', 'fg_subject_unread': '#98971a', 'fg_tags': '#b16286'}

Theme based on the Gruvbox palette (dark background, soft contrast)

gruvbox_light = {'bg': '#fbf1c7', 'bg_alt': '#ebdbb2', 'bg_button': '#ebdbb2', 'bg_highlight': '#fbf1c7', 'fg': '#3c3836', 'fg_bad': '#cc241d', 'fg_bright': '#282828', 'fg_button': '#504945', 'fg_date': '#689d6a', 'fg_dim': '#504945', 'fg_from': '#458588', 'fg_good': '#98971a', 'fg_highlight': '#d79921', 'fg_link': '#b16286', 'fg_subject': '#665c54', 'fg_subject_flagged': '#d65d0e', 'fg_subject_irrelevant': '#928374', 'fg_subject_unread': '#98971a', 'fg_tags': '#b16286'}

Theme based on the Gruvbox palette (light background)

gruvbox_light_hard = {'bg': '#f9f5d7', 'bg_alt': '#ebdbb2', 'bg_button': '#ebdbb2', 'bg_highlight': '#fbf1c7', 'fg': '#3c3836', 'fg_bad': '#cc241d', 'fg_bright': '#282828', 'fg_button': '#504945', 'fg_date': '#689d6a', 'fg_dim': '#504945', 'fg_from': '#458588', 'fg_good': '#98971a', 'fg_highlight': '#d79921', 'fg_link': '#b16286', 'fg_subject': '#665c54', 'fg_subject_flagged': '#d65d0e', 'fg_subject_irrelevant': '#928374', 'fg_subject_unread': '#98971a', 'fg_tags': '#b16286'}

Theme based on the Gruvbox palette (light background, hard contrast)

gruvbox_light_soft = {'bg': '#f2e5bc', 'bg_alt': '#ebdbb2', 'bg_button': '#ebdbb2', 'bg_highlight': '#fbf1c7', 'fg': '#3c3836', 'fg_bad': '#cc241d', 'fg_bright': '#282828', 'fg_button': '#504945', 'fg_date': '#689d6a', 'fg_dim': '#504945', 'fg_from': '#458588', 'fg_good': '#98971a', 'fg_highlight': '#d79921', 'fg_link': '#b16286', 'fg_subject': '#665c54', 'fg_subject_flagged': '#d65d0e', 'fg_subject_irrelevant': '#928374', 'fg_subject_unread': '#98971a', 'fg_tags': '#b16286'}

Theme based on the Gruvbox palette (light background, soft contrast)

nord = {'bg': '#2e3440', 'bg_alt': '#3b4252', 'bg_button': '#4c566a', 'bg_highlight': '#a3be8c', 'fg': '#d8dee9', 'fg_bad': '#bf616a', 'fg_bright': '#b48ead', 'fg_button': '#eceff4', 'fg_date': '#4c566a', 'fg_dim': '#4c566a', 'fg_from': '#5e81ac', 'fg_good': '#a3be8c', 'fg_highlight': '#2e3440', 'fg_link': '#81a1c1', 'fg_subject': '#d8dee9', 'fg_subject_flagged': '#ebcb8b', 'fg_subject_irrelevant': '#4c566a', 'fg_subject_unread': '#b48ead', 'fg_tags': '#81a1c1'}

Theme based on the Nord palette

solarized_dark = {'bg': '#073642', 'bg_alt': '#002b36', 'bg_button': '#002b36', 'bg_highlight': '#eee8d5', 'fg': '#93a1a1', 'fg_bad': '#dc322f', 'fg_bright': '#6c71c4', 'fg_button': '#586e75', 'fg_date': '#2aa198', 'fg_dim': '#586e75', 'fg_from': '#268bd2', 'fg_good': '#859900', 'fg_highlight': '#586e75', 'fg_link': '#6c71c4', 'fg_subject': '#839496', 'fg_subject_flagged': '#6c71c4', 'fg_subject_irrelevant': '#586e75', 'fg_subject_unread': '#eee8d5', 'fg_tags': '#6c71c4'}

Theme based on the Solarized palette (dark background).

solarized_light = {'bg': '#fdf6e3', 'bg_alt': '#fdf6e3', 'bg_button': '#fdf6e3', 'bg_highlight': '#073642', 'fg': '#586e75', 'fg_bad': '#dc322f', 'fg_bright': '#6c71c4', 'fg_button': '#93a1a1', 'fg_date': '#2aa198', 'fg_dim': '#93a1a1', 'fg_from': '#268bd2', 'fg_good': '#859900', 'fg_highlight': '#93a1a1', 'fg_link': '#6c71c4', 'fg_subject': '#839496', 'fg_subject_flagged': '#6c71c4', 'fg_subject_irrelevant': '#586e75', 'fg_subject_unread': '#073642', 'fg_tags': '#6c71c4'}

Theme based on the Solarized palette (light background).

dodo.thread

dodo.util

add_header_line(s, h)

Add the given string to the headers, i.e. before the first blank line, in the provided string.

Return type:

str

body_html(m)

Get the body HTML of a message

Search a message recursively for the first part with content-type equal to “text/html” and return it.

Parameters:

m (dict) – a JSON message

Return type:

str

body_text(m)

Get the body text of a message

Search a message recursively for the first part with content-type equal to “text/plain” and return it.

Parameters:

m (dict) – a JSON message

Return type:

str

clean_html2html(s)

Sanitize the given HTML string

This cleans the input string using Cleaner with the default settings. Set the global util.html2html to this function to enable.

Parameters:

s (str) – an HTML input string

Return type:

str

colorize_text(s, has_headers=False)

Add some colors to HTML-escaped plaintext, for use inside <pre> tag

Return type:

str

decode_header(s)

Decode any charset-encoded parts of an email header

Return type:

str

email_is_me(e)

Check whether the provided email is me

This compares settings.email_address with the provided email, after calling strip_email_address on both. This method is used e.g. by dodo.compose.Compose to filter out the user’s own email when forming a “reply-to-all” message.

Return type:

bool

email_smtp_account_index(e)

Index in settings.smtp_accounts of account having the provided email address

This method is used e.g. by dodo.compose.Compose to autmatically select the account to be used when replying to a mail. It returns the index of first matching account or None if provided email does not match any smtp account.

Return type:

Optional[int]

find_content(m, content_type)

Return a flat list consisting of the ‘content’ field of each message part with the given content-type.

Return type:

List[str]

get_header_addresses(headers, header_keys)

Extract realnames and email addresses from message headers.

The given header_keys are considered, e.g. [‘From’, ‘Reply-To’] to get senders, or [‘To’, ‘Cc’] to get recipients.

Return type:

List[Tuple[str, str]]

html2html(s)

Function used to process HTML messages

This is the identity by default, but can be set to another function to do HTML sanitization, (de)formatting, etc.

html2text(s)

Function used to convert HTML to plain text

This is set to w3m_html2text by default, but can be changed by the user in “config.py”.

Return type:

str

is_attachment(part)

Check whether a message part should be shown an attachment.

All parts with content-disposition equal to “attachment” are considered attachments, except for PGP signatures and parts without a filename.

Some clients also send attachments without a Content-Disposition header. We consider any unknown binary part an attachment.

Return type:

bool

key_string(e)

Convert a Qt keycode plus modifiers into a human readable/writable string

Parameters:

e (QKeyEvent) – a QKeyEvent

Return type:

str

Returns:

a string representing e.key() and its modifiers

linkify(s)

Link URLs and email addresses

Parameters:

s (str) – a plaintext input string

Return type:

str

Returns:

HTML with URLs and emails linked

make_message_css()

Fill placeholders in settings.message_css using the current theme and font settings.

Return type:

str

message_parts(m)

Iterate over JSON message parts recursively, in depth-first order

This is method roughly emulates the behavior of walk, but for JSON representations of an email message rather than Message objects.

Note that if parts are nested, their data will be returned multiple times, first as a sub-object of their parent, then as the part itself.

Parameters:

m (dict) – a JSON message

Return type:

Iterator[dict]

Returns:

an iterator which returns each JSON-subobject corresponding to a message part.

quote_body_text(m)

Return the body text of the message, with ‘>’ prepended to each line

Return type:

str

replace_header(s, h, new_value)

Replace a single header without doing full message parsing

Note this ONLY works for short (i.e. unwrapped) headers.

Return type:

str

sanitize_filename(name)

Replace invalid filename characters.

Note: This should be used for the basename, as it also removes the path separator.

Return type:

str

separate_headers(s)

Split a message into its header part and body part

Return type:

Tuple[str, str]

simple_escape(s)

Provide (limited) HTML escaping

This function only escapes &, <, and >.

Return type:

str

strip_email_address(e)

Strip the display name, leaving just the email address

E.g. “First Last <me@domain.com>” -> “me@domain.com

Return type:

str

w3m_html2text(s)

Convert HTML to plain text using “w3m -dump”

Parameters:

s (str) – an HTML input string

Return type:

str

Returns:

plain text representation of the HTML

wrap_message(s)

Hard wrap message body using wrap_column

Wrap the body part of the message. Headers and quoted text are not affected.

Return type:

str

write_attachments(m)

Write attachments out into temp directory and open with settings.file_browser_command

Currently, this exports a new copy of the attachments every time it is called. Maybe it should do something smarter?

Parameters:

m (dict) – message JSON

Return type:

Tuple[str, List[str]]

Returns:

Return a tuple consisting of the temp dir and a list of files. If no attachments, returns an empty string and empty list.