使用Mutt发送以markdown编写的电子邮件


21

有时我需要在google-group内联上发送一段代码。文字在这里没有帮助;我可以在markdown中键入它,将其转换为html(使用pandoc等),附加为mutt text/html并发送。

这里有一个很好的解决方案但是它使用外部sendmail程序来发送电子邮件。我使用的是mutt,它本身具有通过IMAP发送电子邮件的功能。


1
为什么不仅仅将命令行Markdown格式化程序的输出通过管道传递给sendmail
Naftuli Kay 2014年

嗯..共享计算机!不想存储外部sendmail的密码。
Dilawar 2014年

我们是否可以看到一些示例来说明您在google-groups上的当前结果?
slm

您还想在markdown中输入内容,但是在将它们附加到电子邮件之前先渲染它们,对吗?
slm

这听起来像您的要求,但可能需要修改:dgl.cx/2009/03/html-mail-with-mutt-using-markdown。此外markdownmail.py听起来像的东西,你可以使用。
slm

Answers:


28

撰写邮件之后,但在发送邮件之前,您可以使用许多选项。按?查看它们。

一些可能在这里有所帮助:

  • F 通过外部处理器过滤附件
    • 使用pandoc -s -f markdown -t html转换为HTML
  • ^T 编辑附件的MIME类型
    • 从更改text/plaintext/html

现在,一个宏可以一步完成所有操作。将此添加到您的.muttrc

macro compose \e5 "F pandoc -s -f markdown -t html \ny^T^Utext/html; charset=us-ascii\n"
set wait_key=no

要使用此宏,请在编写完消息之后但在发送之前,按,Esc然后5将markdown格式的消息转换为HTML。

您可以自然地自定义此宏。Mutt已经内置了许多键绑定,因此,无论您选择绑定到哪个键序列,请确保它不会覆盖其他内容(或者您可以不用它)。


该选项在运行外部命令时set wait_key=no禁止Mutt的Press any key to continue...提示。如果wait_keyyes(这是默认设置)Esc,则必须按,然后按,然后按5其他任意键继续。


1
这是一个非常优雅的解决方案!+1
sinisterstuf

5
这很好,但是有核心缺陷。它消除了电子邮件的纯文本部分,这使得它在读取... mutt之类的客户端时很烂;)HTML电子邮件应具有纯文本和html组件。原始markdown应该是纯文本,转换后的应该是HTML。
masukomi

1
同意@masukomi,电子邮件客户端通常会同时发送电子邮件的html和文本版本。最好有一个宏来添加html版本,并将原始文本保留为文本/纯文本。
pepper_chico



1

Sendmail通常不是发送邮件的灵活方式。

我将msmtp与mutt结合使用在特定帐户上,以实现灵活的SMTP。

要将其与Mutt更改一起使用:

# ~/.muttrc  
set sendmail="/usr/bin/msmtp -a default"   

# ~/.msmtprc  
defaults
tls off
logfile ~/.msmtp.log  
account default   
host your.smtp.host  
port 25  
from your-user-name@your-host.com  
auth off  
user username  
password password  

0

我能够做到。我对自己的解决方案并不完全满意,但这已经足够了。等待其他人提供更好的解决方案。

该过程如下。将markdown转换为html并将其附加到邮件。将此附件转为inline附件。但是现在我有两个附件,第一个是markdown,第二个是html。将markdown内容替换为空字符串,以便仅发送html。

我在~/.muttrc文件中添加了以下行。

macro compose B ":set editor=text2mime-markdown.py<enter>E:set editor=email-editor<enter>Da/tmp/html-markdown-alternative.html<enter>^Du"

这是email-editor从有问题的链接中借来的。

#!/bin/sh
if grep -q In-Reply-To $1; then
  # Jump to first line of message
  exec vim -c 'norm }j' $1
else
  # Enter insert mode on the To: line
  exec vim  $1
fi

然后是主要的python文件。这是从相关链接的perl脚本中得到的启发。

#!/usr/bin/env python
import os
import sys
from formatter import *
version = "0.1"

file = sys.argv[1]
new_file = "/tmp/html-markdown-alternative.html"
with open(file, "r") as f:
    text = f.read()

lines = text.split('\n')
header = []
body = []
headerStart = True
for l in lines:
    if headerStart:
        m = re.search(r'^[\w\-]+\:', l)
        if m:
            header.append(l)
        else:
            headerStart = False
            body.append(l)
    else:
        body.append(l)

header = '\n'.join(header)
body = '\n'.join(body)

htmlBody = markdownToHtml(body);

html = []
html.append('<html>')
html.append('<head>')
html.append('<meta name=\"generator\" content=\"text2mime-markdown{}\">'.format(version))
html.append('<style>')
html.append("code { font-family: 'Andale Mono', 'Lucida Console', \
        'Bitstream Vera Sans Mono', 'Courier New', monospace; }")
html.append('pre { border-left: 20px solid #ddd; margin-left: 10px; \
        padding-left: 5px; }')
html.append('</style>')
html.append('</head>')
html.append('<body>')
html.append('{0}'.format(body))
html.append('</body>')
html.append('</html>')
html = '\n'.join(html)

with open(new_file, "w") as newF:
    newF.write(html)

with open(file, 'w') as f:
    f.write(header)

这取决于另一个名为python的python文件formatter.py,该文件pandoc用于格式化我的邮件,但如果pandoc不可用,则可以使用python-markdown2包。该脚本如下。

import subprocess
import re
import os 
import sys
import html2text 
import collections

# check if pandoc exists
panDoc = True
try:
    subprocess.call(["pandoc", '--version']
            , stdout=subprocess.PIPE
            , stdin=subprocess.PIPE
            )
except OSError:
    panDoc = False

if not panDoc:
    import text.html2text as html2text
    import markdown 

def decodeText(text):
    return text.decode('utf-8')

def markdownToHtml(content, convertor='pandoc'):
    global panDoc
    if panDoc:
        cmd = ["pandoc", "-f", "markdown", "-t", "html"]
        p = subprocess.Popen(cmd
                , stdin = subprocess.PIPE
                , stdout = subprocess.PIPE
                )
        p.stdin.write(content)
        content = p.communicate()[0]
        return decodeText(content)
    else:
        return markdown.markdown(decodeText(content))


def htmlToMarkdown(content, convertor='pandoc'):
    global panDoc
    if panDoc and convertor == 'pandoc':
        cmd = ["pandoc", "-t", "markdown", "-f", "html"]
        p = subprocess.Popen(cmd
                , stdin = subprocess.PIPE
                , stdout = subprocess.PIPE
                )
        p.stdin.write(content)
        content = p.communicate()[0]
        return decodeText(content)
    # Use markdown package to convert markdown to html
    else:
        h = html2text.HTML2Text()
        content = h.handle(decodeText(content))
        return content

def titleToBlogDir(title):
    if title is None:
        return ''
    if len(title.strip()) == 0:
        return ''
    blogDir = title.replace(" ","_").replace(':', '-').replace('(', '')
    blogDir = blogDir.replace('/', '').replace('\\', '').replace('`', '')
    blogDir = blogDir.replace(')', '').replace("'", '').replace('"', '')
    return blogDir

def titleToFilePath(title, blogDir):
    if len(blogDir.strip()) == 0:
        return ''
    fileName = os.path.join(blogDir, titleToBlogDir(title))
    return fileName


def htmlToHtml(html):
    return decodeText(html)

def metadataDict(txt):
    mdict = collections.defaultdict(list)
    md = getMetadata(txt)
    for c in ["title", 'type', "layout", "status", "id", "published", "category", "tag"]:
        pat = re.compile(r'{0}\:\s*(?P<name>.+)'.format(c), re.IGNORECASE)
        m = pat.findall(txt)
        for i in m:
            mdict[c].append(i)
    return mdict

def getMetadata(txt):
   """
   Get metadata out of a txt
   """
   if not "---" in txt:
       print txt
       sys.exit(1)

   pat = re.compile(r'\-\-\-+(?P<metadata>.+?)\-\-\-+', re.DOTALL)
   m = pat.search(txt)
   if m:
       return m.group('metadata')
   else:
       sys.exit(0)

def getContent(txt):
    """ 
    Return only text of the post.
    """
    pat = re.compile(r'\-\-\-+(?P<metadata>.+?)\-\-\-+', re.DOTALL)
    return re.sub(pat, "", txt)

def readInputFile(fileName):
    """
    read file and return its format. html or markdown
    """
    assert fileName
    if not os.path.exists(fileName):
        raise IOError, "File %s does not exists" % fileName

    # Check the fmt of file.
    fmt = os.path.splitext(fileName)[1].lower()
    if fmt in ["htm", "html", "xhtml"]:
        fmt = "html"
    elif fmt in ["md", "markdown"]:
        fmt = "markdown"
    else:
        fmt = "markdown"
    txt = open(fileName, 'r').read()   
    return (fmt, txt)

def formatContent(txt, fmt):
    """
    Format the content as per fmt.
    """
    content = getContent(txt)
    if fmt == "html":
        content = htmlToHtml(content)
    elif fmt == "markdown":
        content = markdownToHtml(content)
    else:
        content = markdownToHtml(content)
    return content

这些文件也可以在这里https://github.com/dilawar/mutt


0

我可以使用发送任何格式的电子邮件neomutt。我只是使用Emacs(org-mode)而不是vim。虽然,我也是vim用户。但是,我主要使用Emacs邪恶模式。

在我中,.muttrc我将编辑器设置为emacs而不是vim。编写新电子邮件时neomutt会触发emacs。然后,我将其称为“组织模式”,编写消息,然后导出为所需的任何格式。

我可以导出为PDF格式。然后,我将其保存并附加到PDF我的文件中/tmp。之后,我可以发送给任何人。

如果需要html格式,则以相同的方式导出该格式,然后在发送电子邮件之前自动查看输出。

除此之外,在组织模式下还有许多其他导出格式。只是,选择您想要的。要将代码发送给其他人,只需将源代码添加到所需的任何语言即可。一切都在org-wiki中进行了说明。


0

您也可以同时multipart/alternative包含text/plain和发送电子邮件text/html

要求:pandoc

基本上,它是从markdown消息纯文本和html5创建的。从这些部分创建附件,将它们标记为嵌入式附件,设置正确的mime类型,然后将其合并为mutlipart消息。

在组合菜单中运行此宏后,应该添加任何其他附件。可选地,签名/加密消息应作为最后一步完成

macro compose ,m \
"<enter-command>set pipe_decode<enter>\
<pipe-message>pandoc -f gfm -t plain -o /tmp/msg.txt<enter>\
<pipe-message>pandoc -s -f gfm -t html5 -o /tmp/msg.html<enter>\
<enter-command>unset pipe_decode<enter>a^U/tmp/msg.txt\n^Da^U/tmp/msg.html\n^D^T^Utext/html; charset=utf-8\n=DTT&d^U\n" \
"Convert markdown gfm to HTML and plain" 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.