-
Notifications
You must be signed in to change notification settings - Fork 164
Contrib Hooks
https://github.com/pazz/alot/issues/563#issuecomment-13717496
def pre_envelope_send(ui, dbm):
ADDR = "[email protected]"
BCC_ADDR = "[email protected]"
from_fulladdr = ui.current_buffer.envelope.get_all("From")[0]
if ADDR in from_fulladdr:
ui.current_buffer.envelope.add("Bcc:", BCC_ADDR)
If it bothers you that a search buffer still shows unread messages after you have marked them read in another buffer and return to your search buffer, you may add the following hook:
def pre_buffer_focus(ui, dbm, buf):
if buf.modename == 'search':
buf.rebuild()
To maintain the position of focus in a search buffer after you open another buffer and return to the search buffer use the following hooks (based on pull request #633)
def pre_buffer_open(ui, dbm, buf):
current = ui.current_buffer
if isinstance(current, alot.buffers.SearchBuffer):
current.focused_thread = current.get_selected_thread() # remember focus
def post_buffer_focus(ui, dbm, buf, success):
if success and hasattr(buf, "focused_thread"): # if buffer has saved focus
tid = buf.focused_thread.get_thread_id()
for pos, tlw in enumerate(buf.threadlist.get_lines()):
if tlw.get_thread().get_thread_id() == tid:
buf.body.set_focus(pos)
break
https://github.com/pazz/alot/issues/395#issuecomment-13859844
from twisted.internet.defer import inlineCallbacks
import re
@inlineCallbacks
def pre_envelope_send(ui, dbm):
e = ui.current_buffer.envelope
if re.match('.*[Aa]ttach', e.body, re.DOTALL) and\
not e.attachments:
msg = 'no attachments. send anyway?'
if not (yield ui.choice(msg, select='yes')) == 'yes':
raise Exception()
Sometimes you want to change your From address based on where is the destination. I.e. you want to use work email address for company domain, project email address when addressing some mailing list and personal address otherwise. This will override every other setting:
import re
transitions = [
('.*work-domain\.com.*',
'Your Name <[email protected]>'),
('.*[email protected].*',
'Your Name <[email protected]>')
]
addr_trans = []
for addr, fr in transitions:
addr_trans.append((re.compile("(To|Cc): %s" % addr, re.MULTILINE),
"From: %s" % fr))
def pre_edit_translate(bodytext, ui, dbm):
fromre = re.compile('^From: .*$', re.MULTILINE)
for addr, new_from in addr_trans:
if addr.search(bodytext):
bodytext = re.sub(fromre, new_from,
bodytext)
return bodytext
Github automatically marks a notification as read if one views the corresponding
email in a html-aware mailclient. This is done by a getting a small invisible 1-pixel
beacon image. alot does not download this image per default. If you want to manually
mark some github notification as seen, you can define the hook below and triger
it using call
:
import re
import urllib2
def github_mark_read(ui):
msg = ui.current_buffer.get_selected_message()
msgtext = str(msg.get_email())
r = r"img src='(https://github.com/notifications/beacon/.*.gif)'"
beacons = re.findall(r, msgtext)
if beacons:
urllib2.urlopen(beacons[0])
ui.notify('removed from github notifications:\n %s' % beacons[0])
If you want to mark the focussed message (in thread mode) as read by hitting $
,
add the following to the binding section of the config:
[[bindings]]
[[thread]]
$ = call hooks.github_mark_read(ui)
https://github.com/pazz/alot/issues/656
Recent branches of alot (0.3.5-feature-mailto-666
)
contain helpers alot.helper.parse_mailto
and alot.helper.mailto_to_envelope
.
The hook below uses those to construct unsubscribe mails for mailing lists by
checking if (in thread mode) the currently highlighted message has a List-unsubscribe
header that contains a mailto-string.
With the hooks defines as below, use call hooks.unsubscribe()
to trigger the function.
This can of course also be bound to a keypress.
def unsubscribe():
"""
Unsubscribe from a mailing list.
This hook reads the 'List-Unsubscribe' header of a mail in thread mode,
constructs a unsubsribe-mail according to any mailto-url it finds
and opens the new mail in an envelope buffer.
"""
from alot.helper import mailto_to_envelope
from alot.buffers import EnvelopeBuffer
msg = ui.current_buffer.get_selected_message()
e = msg.get_email()
uheader = e['List-Unsubscribe']
dtheader = e.get('Delivered-To', None)
if uheader is not None:
M = re.search(r'<(mailto:\S*)>', uheader)
if M is not None:
env = mailto_to_envelope(M.group(1))
if dtheader is not None:
env['From'] = [dtheader]
ui.buffer_open(EnvelopeBuffer(ui, env))
else:
ui.notify('focussed mail contains no \'List-Unsubscribe\' header',
'error')
update 09/2017: This does not work any more. Apparently google has shut down the API that the goslate package uses. "Free lunch is over. Thanks for using." (https://pypi.python.org/pypi/goslate) A possible alternative is to use the google API in combination with a dev key directly. Contributions welcome!
[bindings]
[[thread]]
', t' = "call hooks.translate(ui)"
the hook code is below. It uses google translate via goslate: https://pypi.python.org/pypi/goslate .
def translate(ui, targetlang='en'):
# get msg content
msg = ui.current_buffer.get_selected_message()
msgtext = msg.accumulate_body()
# translate
import goslate
gs = goslate.Goslate()
tmsg = gs.translate(msgtext, targetlang)
# replace message widgets content
mt=ui.current_buffer.get_selected_messagetree()
mt.replace_bodytext(tmsg)
mt.refresh()
# refresh the thread buffer
ui.current_buffer.refresh()
To automatically decrypt PGP encrypted emails or remove the PGP header and signature from PGP signed emails, the following hook can be used:
from alot.settings.const import settings
import alot.crypto as crypto
from alot import errors
def text_quote(message):
# avoid importing a big module by using a simple heuristic to guess the
# right encoding
def decode(s, encodings=('ascii', 'utf8', 'latin1')):
for encoding in encodings:
try:
return s.decode(encoding)
except UnicodeDecodeError:
pass
return s.decode('ascii', 'ignore')
lines = message.splitlines()
if len(lines) == 0:
return ""
# delete empty lines at beginning and end (some email client insert these
# outside of the pgp signed message...)
if lines[0] == '' or lines[-1] == '':
from itertools import dropwhile
lines = list(dropwhile(lambda l: l == '', lines))
lines = list(reversed(list(dropwhile(lambda l: l == '', reversed(lines)))))
if len(lines) > 0 and lines[0] == '-----BEGIN PGP MESSAGE-----' \
and lines[-1] == '-----END PGP MESSAGE-----':
try:
sigs, d = crypto.decrypt_verify(message.encode('utf-8'))
message = decode(d)
except errors.GPGProblem:
pass
elif len(lines) > 0 and lines[0] == '-----BEGIN PGP SIGNED MESSAGE-----' \
and lines[-1] == '-----END PGP SIGNATURE-----':
# gpgme does not seem to be able to extract the plain text part of a signed message
import gnupg
gpg = gnupg.GPG()
d = gpg.decrypt(message.encode('utf8'))
message = d.data.decode('utf8')
quote_prefix = settings.get('quote_prefix')
return "\n".join([quote_prefix + line for line in message.splitlines()])
The debbugs bug tracking system as used by gnu.org and debian.org is controlled via email. By default messages to a bug result in an acknowledgement email. To prevent these emails, the X-Debbugs-No-Ack
header has to be added to the emails to the bug server. The following hook automatically adds this header to all emails to [email protected]:
from email.utils import getaddresses
def pre_envelope_send(ui, dbm, cmd):
e = ui.current_buffer.envelope
found = False
for header in ['To', 'Cc', 'Bcc']:
for _, address in getaddresses(e.get_all(header, [])):
if re.match(r'^\[email protected]$', address):
found = True
break
if found:
break
if found:
e.add("X-Debbugs-No-Ack", "kthxbye")
If you need to fetch your email and you've already configured notmuch hooks you may want to add a hook in alot in order to run notmuch new
and fetch the new email when you press a key.
First you need to add some code in alot/hooks.py
in order to define the function getmail
def getmail(ui=None):
ui.notify("fetchinig email..")
msg=subprocess.Popen("notmuch new".split(),stdout=subprocess.PIPE,stderr=subprocess.PIPE)
Then you can map a key in order to do get your emails just adding the following line in your alot/config
file
[bindings]
G = call hooks.getmail(ui)
As an alot developer I run alot directly from git. In order to know which version of the code I am currently running (helpful if you switch branches) I use this hook function in my inital_command
:
import alot
import os.path
import subprocess
def version_notification(ui):
dn = os.path.dirname
directory = dn(dn(alot.__file__))
output = lambda *x: subprocess.check_output(x, cwd=directory).strip()
commit = output('git', 'describe', '--tags')
branch = output('git', 'rev-parse', '--abbrev-ref', 'HEAD')
ui.notify('Version: {}\nGit commit: {}\nGit branch: {}'.format(
alot.__version__, commit, branch), timeout=10)
Useful for viewing and printing HTML emails as they are intended by the sender:
import alot
import tempfile
import webbrowser
from alot.helper import string_sanitize
from alot.helper import string_decode
# Helper method to extract the raw html part of a message. Note that it
# only extracts the first text/html part found.
def _get_raw_html(msg):
mail = msg.get_email()
for part in mail.walk():
ctype = part.get_content_type()
if ctype != "text/html":
continue
cd = part.get('Content-Disposition', '')
if cd.startswith('attachment'):
continue
enc = part.get_content_charset() or 'utf-8'
raw = string_decode(part.get_payload(decode=True), enc)
return string_sanitize(raw)
return None
# Opens HTML emails in an external browser.
# Related issue:
# - https://github.com/pazz/alot/issues/1153
def open_in_browser(ui=None):
ui.notify("Opening message in browser...")
msg = ui.current_buffer.get_selected_message()
htmlstr = _get_raw_html(msg)
if htmlstr == None:
ui.notify("Email has no html part")
return
temp = tempfile.NamedTemporaryFile(prefix="alot-",suffix=".html",
delete=False)
temp.write(htmlstr.encode("utf-8"))
temp.flush()
temp.close()
webbrowser.open(temp.name)