Things
This commit is contained in:
parent
fe27b3b960
commit
86bc146125
|
@ -22,7 +22,7 @@ URxvt*scrollBar: false
|
||||||
|
|
||||||
|
|
||||||
! Font declaration
|
! Font declaration
|
||||||
URxvt.font: xft:DejaVu Sans Mono for Powerline:size=12:antialias=true
|
URxvt.font: xft:DejaVu Sans Mono for Powerline:size=12:antialias=true,xft:Symbola:size=12:antialias=true
|
||||||
|
|
||||||
! Font spacing
|
! Font spacing
|
||||||
URxvt.letterSpace: 0
|
URxvt.letterSpace: 0
|
||||||
|
|
64
bashrc
64
bashrc
|
@ -43,6 +43,9 @@ alias rm='rm -Iv --one-file-system'
|
||||||
alias free='free -m'
|
alias free='free -m'
|
||||||
alias df='df -h'
|
alias df='df -h'
|
||||||
alias dmesg='dmesg --ctime'
|
alias dmesg='dmesg --ctime'
|
||||||
|
alias ffmpeg='ffmpeg -hide_banner'
|
||||||
|
alias ffprobe='ffprobe -hide_banner'
|
||||||
|
alias ffplay='ffplay -hide_banner'
|
||||||
|
|
||||||
# Frequent mistakes
|
# Frequent mistakes
|
||||||
alias sl=ls
|
alias sl=ls
|
||||||
|
@ -62,31 +65,48 @@ alias tracefiles="strace -f -t -e trace=file"
|
||||||
alias n='urxvtc &'
|
alias n='urxvtc &'
|
||||||
|
|
||||||
# Superseding commands with better ones if they are present
|
# Superseding commands with better ones if they are present
|
||||||
function vi() {
|
function _do_rank() { # executables... -- arguments...
|
||||||
if which vim &> /dev/null; then
|
for ex in "$@"
|
||||||
alias vi='vim'
|
do
|
||||||
fi
|
[ "$ex" == "--" ] && break
|
||||||
vim "$@"
|
if which "$ex" &> /dev/null
|
||||||
|
then
|
||||||
|
for al in "$@"
|
||||||
|
do
|
||||||
|
shift
|
||||||
|
[ "$al" == "--" ] && break
|
||||||
|
alias "$al"="$ex"
|
||||||
|
done
|
||||||
|
"$ex" "$@"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
for ex in "$@"
|
||||||
|
do
|
||||||
|
[ "$al" == "--" ] && break
|
||||||
|
if -z "$list"
|
||||||
|
then
|
||||||
|
list=$ex
|
||||||
|
else
|
||||||
|
list=$list, $ex
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "Not installed: $list"
|
||||||
}
|
}
|
||||||
function pass() {
|
|
||||||
if which gopass &> /dev/null; then
|
function _install_rank() { # executables...
|
||||||
alias pass='gopass'
|
for ex in "$@"
|
||||||
fi
|
do
|
||||||
gopass "$@"
|
list=$@
|
||||||
}
|
alias "$ex"="_do_rank $list --"
|
||||||
function wol() {
|
done
|
||||||
if which wakeonlan &> /dev/null; then
|
|
||||||
alias wol='wakeonlan'
|
|
||||||
fi
|
|
||||||
wakeonlan "$@"
|
|
||||||
}
|
|
||||||
function mutt() {
|
|
||||||
if which neomutt &> /dev/null; then
|
|
||||||
alias mutt='neomutt'
|
|
||||||
fi
|
|
||||||
neomutt "$@"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_install_rank nvim vim vi
|
||||||
|
_install_rank gopass pass
|
||||||
|
_install_rank wakeonlan wol
|
||||||
|
_install_rank neomutt mutt
|
||||||
|
|
||||||
# SHELL CUSTOMIZATION
|
# SHELL CUSTOMIZATION
|
||||||
|
|
||||||
complete -cf sudo
|
complete -cf sudo
|
||||||
|
|
141
scripts/mel
Executable file
141
scripts/mel
Executable file
|
@ -0,0 +1,141 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
Meh mail client
|
||||||
|
"""
|
||||||
|
|
||||||
|
import notmuch
|
||||||
|
import logging
|
||||||
|
import coloredlogs
|
||||||
|
import colorama
|
||||||
|
import datetime
|
||||||
|
import os
|
||||||
|
import progressbar
|
||||||
|
import time
|
||||||
|
|
||||||
|
colorama.init()
|
||||||
|
coloredlogs.install(level='DEBUG', fmt='%(levelname)s %(message)s')
|
||||||
|
log = logging.getLogger()
|
||||||
|
|
||||||
|
log.debug("Loading database")
|
||||||
|
|
||||||
|
db = notmuch.Database(mode=notmuch.Database.MODE.READ_WRITE)
|
||||||
|
# TODO Open read-only when needed
|
||||||
|
|
||||||
|
log.debug("Database loaded")
|
||||||
|
|
||||||
|
def get_location(msg):
|
||||||
|
path = msg.get_filename()
|
||||||
|
path = os.path.dirname(path)
|
||||||
|
base = db.get_path()
|
||||||
|
assert path.startswith(base)
|
||||||
|
path = path[len(base):]
|
||||||
|
_, mailbox, folder, state = path.split('/')
|
||||||
|
assert state in {'cur', 'tmp', 'new'}
|
||||||
|
return (mailbox, folder, state)
|
||||||
|
|
||||||
|
MAILBOX_COLORS = dict()
|
||||||
|
|
||||||
|
def get_mailbox_color(mailbox):
|
||||||
|
if mailbox not in MAILBOX_COLORS:
|
||||||
|
colorfile = os.path.join(db.get_path(), mailbox, 'color')
|
||||||
|
assert os.path.isfile(colorfile)
|
||||||
|
with open(colorfile, 'r') as f:
|
||||||
|
colorStr = f.read()
|
||||||
|
colorStr = colorStr[1:] if colorStr[0] == '#' else colorStr
|
||||||
|
R = int(colorStr[0:2], 16)
|
||||||
|
G = int(colorStr[2:4], 16)
|
||||||
|
B = int(colorStr[4:6], 16)
|
||||||
|
MAILBOX_COLORS[mailbox] = '\x1b[38;2;{};{};{}m'.format(R, G, B)
|
||||||
|
return MAILBOX_COLORS[mailbox]
|
||||||
|
|
||||||
|
def format_date(date):
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
midnight = datetime.datetime(year=now.year, month=now.month, day=now.day)
|
||||||
|
if date > midnight:
|
||||||
|
return date.strftime('%H:%M:%S')
|
||||||
|
else:
|
||||||
|
return date.strftime('%d/%m/%y')
|
||||||
|
|
||||||
|
|
||||||
|
def print_msg(msg):
|
||||||
|
line = ""
|
||||||
|
tags = set(msg.get_tags())
|
||||||
|
mailbox, folder, state = get_location(msg)
|
||||||
|
line += get_mailbox_color(mailbox)
|
||||||
|
|
||||||
|
# Date
|
||||||
|
date = datetime.datetime.fromtimestamp(msg.get_date())
|
||||||
|
line += format_date(date)
|
||||||
|
|
||||||
|
# Icons
|
||||||
|
line += " "
|
||||||
|
def tags2col1(tag1, tag2, both, first, second, none):
|
||||||
|
nonlocal line
|
||||||
|
if tag1 in tags:
|
||||||
|
if tag2 in tags:
|
||||||
|
line += both
|
||||||
|
else:
|
||||||
|
line += first
|
||||||
|
else:
|
||||||
|
if tag2 in tags:
|
||||||
|
line += second
|
||||||
|
else:
|
||||||
|
line += none
|
||||||
|
|
||||||
|
tags2col1('spam', 'draft', '??', '💥', '📝', ' ')
|
||||||
|
tags2col1('attachment', 'encrypted', '🔐', '📎', '🔑', ' ')
|
||||||
|
tags2col1('unread', 'flagged', '🏁', '🏳 ', '🏴', ' ')
|
||||||
|
tags2col1('sent', 'replied', '?', '↑', '↪', ' ')
|
||||||
|
|
||||||
|
# TODO To: / From:
|
||||||
|
|
||||||
|
# Subject
|
||||||
|
line += " "
|
||||||
|
line += msg.get_header("subject")
|
||||||
|
line += colorama.Style.RESET_ALL
|
||||||
|
print(line)
|
||||||
|
|
||||||
|
|
||||||
|
def retag_msg(msg):
|
||||||
|
msg.freeze()
|
||||||
|
mailbox, folder, state = get_location(msg)
|
||||||
|
|
||||||
|
# Search-friendly folder name
|
||||||
|
if folder.startswith('INBOX.'):
|
||||||
|
folder = folder[6:]
|
||||||
|
folder = folder.upper()
|
||||||
|
|
||||||
|
msg.remove_all_tags()
|
||||||
|
if folder.startswith('JUNK') or folder.startswith('SPAM'):
|
||||||
|
msg.add_tag('spam')
|
||||||
|
if folder.startswith('DRAFT'):
|
||||||
|
msg.add_tag('draft')
|
||||||
|
if folder.startswith('INBOX'):
|
||||||
|
msg.add_tag('inbox')
|
||||||
|
|
||||||
|
# TODO 'sent' tag
|
||||||
|
|
||||||
|
# Save
|
||||||
|
msg.thaw()
|
||||||
|
msg.tags_to_maildir_flags()
|
||||||
|
|
||||||
|
|
||||||
|
def applyMsgs(queryStr, function, useProgressbar=False):
|
||||||
|
query = notmuch.Query(db, queryStr)
|
||||||
|
query.set_sort(notmuch.Query.SORT.OLDEST_FIRST)
|
||||||
|
msgs = query.search_messages()
|
||||||
|
if useProgressbar:
|
||||||
|
nbMsgs = query.count_messages()
|
||||||
|
iterator = progressbar.progressbar(msgs, max_value=nbMsgs)
|
||||||
|
else:
|
||||||
|
iterator = msgs
|
||||||
|
for msg in iterator:
|
||||||
|
function(msg)
|
||||||
|
|
||||||
|
applyMsgs('tag:inbox', print_msg)
|
||||||
|
# applyMsgs('tag:spam', print_msg)
|
||||||
|
# applyMsgs('tag:unread', print_msg)
|
||||||
|
# applyMsgs('from:geoffrey@frogeye.fr', print_msg)
|
||||||
|
|
||||||
|
# applyMsgs('*', retag_msg, useProgressbar=True)
|
176
scripts/melConf
Executable file
176
scripts/melConf
Executable file
|
@ -0,0 +1,176 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
Meh mail client conf generator for other things
|
||||||
|
"""
|
||||||
|
|
||||||
|
import configparser
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# TODO Find config file from XDG
|
||||||
|
# TODO Alias adresses
|
||||||
|
# TODO Signature file
|
||||||
|
# TODO Write ~/.mail/[mailbox]/color file if required by sth?
|
||||||
|
# Certificate file
|
||||||
|
|
||||||
|
configPath = os.path.join(os.path.expanduser('~'), '.config', 'mel.conf')
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(configPath)
|
||||||
|
|
||||||
|
SERVER_DEFAULTS = {
|
||||||
|
"imap": {"port": 143, "starttls": True},
|
||||||
|
"smtp": {"port": 587, "starttls": True},
|
||||||
|
}
|
||||||
|
SERVER_ITEMS = {"host", "port", "user", "pass", "starttls"}
|
||||||
|
|
||||||
|
# Reading sections
|
||||||
|
accounts = dict()
|
||||||
|
|
||||||
|
for name in config.sections():
|
||||||
|
if not name.islower():
|
||||||
|
continue
|
||||||
|
section = config[name]
|
||||||
|
|
||||||
|
data = dict()
|
||||||
|
for server in SERVER_DEFAULTS.keys():
|
||||||
|
for item in SERVER_ITEMS:
|
||||||
|
key = server + item
|
||||||
|
try:
|
||||||
|
val = section.get(key) or section.get(item) or SERVER_DEFAULTS[server][item]
|
||||||
|
except KeyError:
|
||||||
|
raise KeyError("{}.{}".format(name, key))
|
||||||
|
|
||||||
|
if isinstance(val, str):
|
||||||
|
if val == "True":
|
||||||
|
val = True
|
||||||
|
elif val == "False":
|
||||||
|
val = False
|
||||||
|
elif val.isnumeric():
|
||||||
|
val = int(val)
|
||||||
|
data[key] = val
|
||||||
|
|
||||||
|
for key in section.keys():
|
||||||
|
if key in SERVER_ITEMS:
|
||||||
|
continue
|
||||||
|
data[key] = section[key]
|
||||||
|
|
||||||
|
data["name"] = name
|
||||||
|
data["storage"] = os.path.join(config['GLOBAL']['storage'], name)
|
||||||
|
data["storageInbox"] = os.path.join(data["storage"], "INBOX")
|
||||||
|
storageFull = os.path.expanduser(data["storage"])
|
||||||
|
os.makedirs(storageFull, exist_ok=True)
|
||||||
|
accounts[name] = data
|
||||||
|
|
||||||
|
# OfflineIMAP
|
||||||
|
|
||||||
|
OFFLINEIMAP_BEGIN = """[general]
|
||||||
|
# List of accounts to be synced, separated by a comma.
|
||||||
|
accounts = {}
|
||||||
|
maxsyncaccounts = {}
|
||||||
|
stocktimeout = 60
|
||||||
|
pythonfile = ~/.config/offlineimap.py
|
||||||
|
|
||||||
|
[mbnames]
|
||||||
|
enabled = yes
|
||||||
|
filename = ~/.mutt/mailboxes
|
||||||
|
header = "mailboxes "
|
||||||
|
peritem = "+%(accountname)s/%(foldername)s"
|
||||||
|
sep = " "
|
||||||
|
footer = "\\n"
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
OFFLINEIMAP_ACCOUNT = """[Account {name}]
|
||||||
|
localrepository = {name}-local
|
||||||
|
remoterepository = {name}-remote
|
||||||
|
autorefresh = 0.5
|
||||||
|
quick = 10
|
||||||
|
utf8foldernames = yes
|
||||||
|
postsynchook = ~/.mutt/postsync
|
||||||
|
|
||||||
|
[Repository {name}-local]
|
||||||
|
type = Maildir
|
||||||
|
localfolders = {storage}
|
||||||
|
|
||||||
|
[Repository {name}-remote]
|
||||||
|
type = IMAP
|
||||||
|
{secconf}
|
||||||
|
keepalive = 60
|
||||||
|
holdconnectionopen = yes
|
||||||
|
remotehost = {imaphost}
|
||||||
|
remoteport = {imapport}
|
||||||
|
remoteuser = {imapuser}
|
||||||
|
remotepass = {imappass}
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
offlineIMAPstr = OFFLINEIMAP_BEGIN.format(','.join(accounts), len(accounts))
|
||||||
|
for name, account in accounts.items():
|
||||||
|
if account["imapstarttls"]:
|
||||||
|
secconf = "ssl = no"
|
||||||
|
else:
|
||||||
|
secconf = "sslcacertfile = /etc/ssl/certs/ca-certificates.crt"
|
||||||
|
offlineIMAPstr += OFFLINEIMAP_ACCOUNT.format(**account, secconf=secconf)
|
||||||
|
# TODO Write
|
||||||
|
|
||||||
|
# mbsync
|
||||||
|
MBSYNC_ACCOUNT = """IMAPAccount {name}
|
||||||
|
Host {imaphost}
|
||||||
|
User {imapuser}
|
||||||
|
Pass "{imappass}"
|
||||||
|
{secconf}
|
||||||
|
|
||||||
|
IMAPStore {name}-remote
|
||||||
|
Account {name}
|
||||||
|
|
||||||
|
MaildirStore {name}-local
|
||||||
|
Subfolders Verbatim
|
||||||
|
Path {storage}/
|
||||||
|
Inbox {storageInbox}/
|
||||||
|
|
||||||
|
Channel {name}
|
||||||
|
Master :{name}-remote:
|
||||||
|
Slave :{name}-local:
|
||||||
|
Patterns *
|
||||||
|
Create Both
|
||||||
|
SyncState *
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
mbsyncStr = ""
|
||||||
|
for name, account in accounts.items():
|
||||||
|
if account["imapstarttls"]:
|
||||||
|
secconf = "SSLType STARTTLS"
|
||||||
|
else:
|
||||||
|
secconf = "SSLType IMAPS"
|
||||||
|
mbsyncStr += MBSYNC_ACCOUNT.format(**account, secconf=secconf)
|
||||||
|
msbsyncFilepath = os.path.join(os.path.expanduser('~'), '.mbsyncrc')
|
||||||
|
with open(msbsyncFilepath, 'w') as f:
|
||||||
|
f.write(mbsyncStr)
|
||||||
|
|
||||||
|
# msmtp
|
||||||
|
MSMTP_BEGIN = """defaults
|
||||||
|
protocol smtp
|
||||||
|
auth on
|
||||||
|
tls_trust_file /etc/ssl/certs/ca-certificates.crt
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
MSMTP_ACCOUNT = """account {name}
|
||||||
|
from {from}
|
||||||
|
user {smtpuser}
|
||||||
|
password {smtppass}
|
||||||
|
host {smtphost}
|
||||||
|
port {smtpport}
|
||||||
|
tls on
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
msmtpStr = MSMTP_BEGIN
|
||||||
|
for name, account in accounts.items():
|
||||||
|
msmtpStr += MSMTP_ACCOUNT.format(**account)
|
||||||
|
msbsyncFilepath = os.path.join(os.path.expanduser('~'), '.msmtprc')
|
||||||
|
with open(msbsyncFilepath, 'w') as f:
|
||||||
|
f.write(msmtpStr)
|
|
@ -146,20 +146,26 @@ do
|
||||||
|
|
||||||
done <<< "$(find "$dir" -type f -iname "*.png")"
|
done <<< "$(find "$dir" -type f -iname "*.png")"
|
||||||
|
|
||||||
# SVG (requires svgo)
|
# # SVG (requires scour)
|
||||||
while read image
|
# while read image
|
||||||
do
|
# do
|
||||||
if [ -z "$image" ]; then continue; fi
|
# if [ -z "$image" ]; then continue; fi
|
||||||
echo Processing $image
|
# echo Processing $image
|
||||||
|
#
|
||||||
|
# temp=$(mktemp --suffix .svg)
|
||||||
|
# scour --quiet "$image" "$temp" --no-line-breaks
|
||||||
|
# echo "→ Optimize done"
|
||||||
|
#
|
||||||
|
# replaceImg "$temp" "$image"
|
||||||
|
#
|
||||||
|
# done <<< "$(find "$dir" -type f -iname "*.svg")"
|
||||||
|
|
||||||
temp=$(mktemp --suffix .svg)
|
# NOTE Explicitely disabled since:
|
||||||
cp "$image" "$temp"
|
# - I only have ~50 MiB of SVG in TOTAL
|
||||||
svgo --quiet --config $HOME/.config/optiSvgo.yml "$temp"
|
# - Most conversions are not image losseless
|
||||||
echo "→ Optimize done"
|
# - Even when they are losseless, they are mostly worthless
|
||||||
|
# - I might want to keep editor data and/or ids for some of them
|
||||||
replaceImg "$temp" "$image"
|
# So rather use scour explicitely when needed
|
||||||
|
|
||||||
done <<< "$(find "$dir" -type f -iname "*.svg")"
|
|
||||||
|
|
||||||
cleandev
|
cleandev
|
||||||
|
|
||||||
|
|
2
vimrc
2
vimrc
|
@ -52,7 +52,7 @@ else
|
||||||
endif
|
endif
|
||||||
Plug 'zchee/deoplete-jedi'
|
Plug 'zchee/deoplete-jedi'
|
||||||
|
|
||||||
Plug 'python-mode/python-mode', { 'branch': 'develop' }
|
" Plug 'python-mode/python-mode', { 'branch': 'develop' }
|
||||||
Plug 'junegunn/fzf', {'do': './install --bin'}
|
Plug 'junegunn/fzf', {'do': './install --bin'}
|
||||||
Plug 'junegunn/fzf.vim'
|
Plug 'junegunn/fzf.vim'
|
||||||
Plug 'ervandew/supertab'
|
Plug 'ervandew/supertab'
|
||||||
|
|
Loading…
Reference in a new issue