nix: Make nix the root

Which means now I'll have to think about real prefixes in commit names.
This commit is contained in:
Geoffrey Frogeye 2023-11-26 23:58:22 +01:00
parent 550eed06e0
commit ee178b7d57
Signed by: geoffrey
GPG key ID: C72403E7F82E6AD8
190 changed files with 5 additions and 6 deletions

View file

@ -0,0 +1,50 @@
[General]
check_for_update=true
hide_on_close=false
minimize_to_systray=false
show_systray_icon=true
start_minimized=false
[content]
custom_css_file=
dark_mode=false
default_fixed_font_size=13
default_font_family=serif
default_font_size=16
disable_ad=false
external_link_policy=@Variant(\0\0\0\x7f\0\0\0)Zeal::Core::Settings::ExternalLinkPolicy\0\0\0\0\0)
fixed_font_family=DejaVu Sans Mono
highlight_on_navigate=true
minimum_font_size=0
sans_serif_font_family=DejaVu Sans
serif_font_family=DejaVu Serif
smooth_scrolling=false
[docsets]
path=/home/geoffrey/.cache/dash_docsets
[global_shortcuts]
show=
[internal]
install_id=a2353684-8909-4ac3-8ad5-b8d8a7e9292a
version=0.6.1
[proxy]
authenticate=false
host=
password=
port=0
type=1
username=
[search]
fuzzy_search_enabled=false
[state]
splitter_geometry=@ByteArray(\0\0\0\xff\0\0\0\x1\0\0\0\x2\0\0\x1\0\0\0\x4\xb0\0\xff\xff\xff\xff\x1\0\0\0\x1\0)
toc_splitter_state=@ByteArray()
window_geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\a\x80\0\0\0\x12\0\0\f\x7f\0\0\x3\xeb\0\0\a\x80\0\0\0\x12\0\0\f\x7f\0\0\x3\xeb\0\0\0\0\0\0\0\0\x5\0\0\0\a\x80\0\0\0\x12\0\0\f\x7f\0\0\x3\xeb)
[tabs]
open_new_tab_after_active=false

View file

@ -0,0 +1 @@
self_name

View file

@ -0,0 +1,10 @@
# Automatrop
Because I'm getting tired of too many bash scripts and yet using Ansible seems
overkill at the same time.
## Dependencies
```bash
ansible-galaxy install mnussbaum.base16-builder-ansible
```

View file

@ -0,0 +1,9 @@
[defaults]
inventory=hosts
roles_path=roles
interpreter_python=auto
library=plugins/modules
[ssh_connection]
pipelining = True # does not work with requiretty in /etc/sudoers
ssh_args=-o ForwardAgent=yes # no need for installing/configuring/unlocking SSH/GPG keys on the host to be able to git clone extensions

View file

@ -0,0 +1,34 @@
# Default values
# If you have root access on the machine (via sudo)
root_access: no
# Display server (no, "x11", "wayland")
display_server: no
# What development work will I do on this machine
dev_stuffs: []
# Install software that is rarely used
software_full: no
# Which additional software to install
software_snippets: []
# If the computer has a battery and we want to use it
has_battery: no
# Activate numlock by default
auto_numlock: no
# Machine has SSH key to access git.frogeye.fr
has_forge_access: no
# Wether to permit /home/$USER to be encrypted
# with stacked filesystem encryption
encrypt_home_stacked_fs: no
# Which extensions to load
extensions: []
# TODO Make role/playbook defaults instead

View file

@ -0,0 +1,30 @@
root_access: yes
display_server: "x11"
dev_stuffs:
- ansible
- docker
- network
- nix
- perl
- php
- python
- shell
- sql
software_full: yes
has_battery: yes
auto_numlock: yes
has_forge_access: yes
extensions:
- g
- gh
x11_screens:
# nvidia-xrun
# - HDMI-0
# - eDP-1-1
# mesa + nouveau
# - HDMI-1-3
# - eDP1
# mesa + nvidia
- HDMI-1-0
- eDP1
max_video_height: 1440

View file

@ -0,0 +1,14 @@
root_access: no
display_server: "x11"
dev_stuffs:
- shell
- network
- ansible
- perl
- python
extensions:
- gh
x11_screens:
- HDMI-1
- HDMI-2
base16_scheme: solarized-light

View file

@ -0,0 +1,16 @@
root_access: yes
display_server: "x11"
dev_stuffs:
- shell
- network
- ansible
- python
has_battery: yes
encrypt_home_stacked_fs: yes
extensions:
- g
- gh
x11_screens:
- DP-1
- eDP-1
max_video_height: 720

View file

@ -0,0 +1,4 @@
curacao.geoffrey.frogeye.fr
# triffle.geoffrey.frogeye.fr
pindakaas.geoffrey.frogeye.fr
gho.geoffrey.frogeye.fr ansible_host=localhost ansible_port=2222

View file

@ -0,0 +1,15 @@
---
- name: Default
hosts: all
roles:
- role: system
tags: system
when: root_access
- role: dotfiles
tags: dotfiles
- role: termux
tags: termux
when: termux
- role: extensions
tags: extensions
# TODO Dependencies

View file

@ -0,0 +1,3 @@
---
- name: Install dotfiles
ansible.builtin.command: "{{ ansible_user_dir }}/.dotfiles/config/scripts/dotfiles install"

View file

@ -0,0 +1,9 @@
---
- name: Install dotfiles repository
ansible.builtin.git:
repo: "{% if has_forge_access %}git@git.frogeye.fr:{% else %}https://git.frogeye.fr/{% endif %}geoffrey/dotfiles.git"
dest: "{{ ansible_user_dir }}/.dotfiles"
update: true
notify: install dotfiles
tags: dotfiles_repo
# TODO Put actual dotfiles in a subdirectory of the repo, so we don't have to put everything in config

View file

@ -0,0 +1,14 @@
---
- name: Load extensions
ansible.builtin.include_role:
name: geoffreyfrogeye.{{ extension }}automatrop.entry
loop: "{{ extensions }}"
loop_control:
loop_var: extension
tags: always
- name: Configure extensions rc sourcing
ansible.builtin.template:
src: extrc.sh.j2
dest: "{{ ansible_user_dir }}/.config/shell/extrc"
mode: u=rw,g=r,o=r

View file

@ -0,0 +1,7 @@
#!/usr/bin/env bash
{% for extension in extensions %}
trysource ~/.config/{{ extension }}scripts/{{ extension }}profile
{% endfor %}
{# TODO Rename profile as rc, and add env #}
{# TODO Put in non-linked path #}

View file

@ -0,0 +1,2 @@
[Service]
ExecStartPre=/bin/sh -c 'setleds +num < /dev/%I'

View file

@ -0,0 +1,5 @@
Section "Device"
Identifier "Intel Graphics"
Driver "intel"
Option "Backlight" "intel_backlight"
EndSection

View file

@ -0,0 +1,15 @@
Section "InputClass"
Identifier "joystick catchall"
MatchIsJoystick "on"
MatchDevicePath "/dev/input/event*"
Driver "joystick"
Option "StartKeysEnabled" "False" #Disable mouse
Option "StartMouseEnabled" "False" #support
EndSection
# Same thing for DualShock 4 touchpad
Section "InputClass"
Identifier "ds4-touchpad"
Driver "libinput"
MatchProduct "Wireless Controller Touchpad"
Option "Ignore" "True"
EndSection

View file

@ -0,0 +1,10 @@
- name: Reload systemd daemon
ansible.builtin.systemd:
daemon_reload: true
listen: systemd changed
become: true
- name: Warn about changed Panfrost config
ansible.builtin.debug:
msg: The Panfrost display driver configuration was changed, but needs a reboot to be applied.
listen: panfrost config changed

View file

@ -0,0 +1,70 @@
# Xorg configuration
- name: Check if there is Intel backlight
ansible.builtin.stat:
path: /sys/class/backlight/intel_backlight
register: intel_backlight
when: display_server == 'x11'
- name: Install Intel video drivers (Arch based)
community.general.pacman:
name: xf86-video-intel
# state: "{{ intel_backlight.stat.exists }}"
state: present
become: true
when: display_server == 'x11' and intel_backlight.stat.exists and arch_based
# TODO With software role? Would permit other distributions
- name: Configure Xorg Intel backlight
ansible.builtin.copy:
src: xorg/intel_backlight.conf
dest: "{{ item }}/20-intel_backlight.conf"
become: true
when: display_server == 'x11' and intel_backlight.stat.exists
loop: "{{ xorg_common_config_dirs }}"
- name: Configure Xorg joystick behaviour
ansible.builtin.copy:
src: xorg/joystick.conf
dest: "{{ item }}/50-joystick.conf"
become: true
when: display_server == 'x11'
loop: "{{ xorg_common_config_dirs }}"
- name: List modules we're using
ansible.builtin.slurp:
src: /proc/modules
register: modules
when: display_server
# Not sure the module will be loaded in early setup stages though
- name: Make panfrost use OpenGL 3.3
ansible.builtin.lineinfile:
path: /etc/environment
line: PAN_MESA_DEBUG="gl3"
regexp: ^#? ?PAN_MESA_DEBUG=
become: true
when: display_server and using_panfrost
vars:
using_panfrost: "{{ 'panfrost' in (modules.content | b64decode) }}"
notify: panfrost config changed
# Numlock on boot
- name: Set numlock on boot
ansible.builtin.copy:
src: getty.service
dest: /etc/systemd/system/getty@.service.d/override.conf
become: true
notify:
- systemd changed
when: auto_numlock
- name: Unset numlock on boot
ansible.builtin.file:
path: /etc/systemd/system/getty@.service.d/override.conf
state: absent
become: true
notify:
- systemd changed
when: not auto_numlock

View file

@ -0,0 +1,29 @@
---
- name: Create Termux directory
ansible.builtin.file:
state: directory
path: "{{ ansible_user_dir }}/.termux"
mode: u=rwx,g=rx,o=rx
# TODO This is a dotfiles directory.
# Make it not install unless it's Termux
- name: Silence Termux login message
ansible.builtin.file:
state: file
path: "{{ ansible_user_dir }}/.hushlogin"
mode: u=rw,g=r,o=r
# https://github.com/kdrag0n/base16-termux/blob/master/templates/default.mustache
- name: Download base16 theme for Termux
ansible.builtin.copy:
content: "{{ base16_schemes['schemes'][base16_scheme]['termux']['colors']['base16-' + base16_scheme + '.properties'] }}"
dest: "{{ ansible_env.HOME }}/.termux/colors.properties"
mode: u=rw,g=r,o=r
tags:
- color
# TODO
# Upgrade
# If root:
# $ apt install tsu
# $ echo '/system/bin/mount -o remount,rw /; ln -s /data/data/com.termux/files/usr /usr; /system/bin/mount -o remount,ro /' | tsu

View file

@ -0,0 +1,44 @@
[general]
status_path = "~/.cache/vdirsyncer/status/"
{% for config in configs %}
# CarDAV
[pair geoffrey_contacts]
a = "geoffrey_contacts_local"
b = "geoffrey_contacts_remote"
collections = ["from a", "from b"]
metadata = ["displayname"]
[storage geoffrey_contacts_local]
type = "filesystem"
path = "~/.cache/vdirsyncer/contacts/"
fileext = ".vcf"
[storage geoffrey_contacts_remote]
type = "carddav"
url = "https://cloud.frogeye.fr/remote.php/dav"
username = "geoffrey"
password.fetch = ["command", "sh", "-c", "cat ~/.config/vdirsyncer/pass"]
# CalDAV
[pair geoffrey_calendar]
a = "geoffrey_calendar_local"
b = "geoffrey_calendar_remote"
collections = ["from a", "from b"]
metadata = ["displayname", "color"]
[storage geoffrey_calendar_local]
type = "filesystem"
path = "~/.cache/vdirsyncer/calendars/"
fileext = ".ics"
[storage geoffrey_calendar_remote]
type = "caldav"
url = "https://cloud.frogeye.fr/remote.php/dav"
username = "geoffrey"
password.fetch = ["command", "sh", "-c", "cat ~/.config/vdirsyncer/pass"]
{% endfor %}

View file

View file

@ -0,0 +1,4 @@
# https://i.imgur.com/yVtVucs.jpg # Doctor Who Series 11
# Derivate of these ones https://wallpapers.wallhaven.cc/wallpapers/full/wallhaven-230622.png
# https://geoffrey.frogeye.fr/files/backgrounds/VertBleu.png
https://geoffrey.frogeye.fr/files/backgrounds/BleuVert.png

View file

@ -0,0 +1,32 @@
#!/usr/bin/env bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
# From https://stackoverflow.com/a/246128
# Relaunch the bars
# i3-msg exec ~/.config/polybar/launch.sh
# TODO Make something better with that
i3-msg exec ~/.config/lemonbar/launch.sh
# Resize background
BGDIR="$HOME/.cache/background"
mkdir -p "$BGDIR"
list="$DIR/bg"
url="$(cat "$list" | sed -e 's/#.*$//' -e 's/ \+$//' -e '/^$/d' | sort -R | head -1)"
hash="$(printf "$url" | md5sum | cut -f1 -d' ')"
filepath="$BGDIR/$hash"
if [ ! -e "$filepath" ]; then
wget -c "$url" -O "$filepath"
fi
feh --no-fehbg --bg-fill "$filepath"
# Make i3 distribute the workspaces on all screens
monitors_json="$(xrandr --listmonitors | tail -n+2 | awk '{ print $4 }' | sed 's|.\+|"\0"|' | tr '\n' ',')"
automatrop -e '{"x11_screens":['"$monitors_json"']}' --tags i3
# TODO Make sure it goes from left to right
# Either with the "main" display or using the geometry data

View file

@ -0,0 +1,26 @@
[Plugins]
Output=i3bar
Input=nm;pulseaudio;upower;time
Order=nm;pulseaudio;upower;time
[Time]
Zones=Europe/Paris;
[PulseAudio]
#Actions=raise
[NetworkManager]
Interfaces=enp3s0;wlp2s0
HideUnavailable=true
[Override pulseaudio:auto_null]
Label=🔊
[Override nm-wifi:wlp2s0]
Label=📶
[Override time:Europe/Paris]
Label=🕗
[Override upower-battery:BAT0]
Label=🔋

View file

@ -0,0 +1,34 @@
[calendars]
[[calendars]]
path = ~/.cache/vdirsyncer/calendars/*
type = discover
# [[birthdays]]
# type=birthdays
# path = ~/.cache/vdirsyncer/contacts/contacts/
# color = light magenta
[locale]
timeformat = %H:%M
dateformat = %d/%m
longdateformat = %d/%m/%Y
datetimeformat = %d/%m %H:%M
longdatetimeformat = %d/%m/%Y %H:%M
local_timezone = Europe/Paris
[default]
default_calendar = "Personnel"
default_command = calendar
highlight_event_days = True
show_all_days = True
timedelta = 7d
[highlight_days]
[view]
agenda_day_format = "{bold}{name}, {date-long}{reset}"
agenda_event_format = "{calendar-color}{cancelled}{start-end-time-style} {title}{repeat-symbol} | {location}{reset}"
bold_for_light_color = False
event_format = "{calendar-color}{cancelled}{start}-{end} {title}{repeat-symbol} | {location}{reset}"
event_view_always_visible = True
frame = color

View file

@ -0,0 +1,43 @@
# example configuration file for khard version >= 0.11.0
# place it under $HOME/.config/khard/khard.conf
[addressbooks]
[[contacts]]
path = ~/.cache/vdirsyncer/contacts/contacts/
[general]
debug = no
default_action = list
editor = nvim
merge_editor = nvim
[contact table]
# display names by first or last name: first_name / last_name
display = first_name
# group by address book: yes / no
group_by_addressbook = no
# reverse table ordering: yes / no
reverse = no
# append nicknames to name column: yes / no
show_nicknames = no
# show uid table column: yes / no
show_uids = yes
# sort by first or last name: first_name / last_name
sort = last_name
# localize dates: yes / no
localize_dates = yes
[vcard]
# extend contacts with your own private objects
# these objects are stored with a leading "X-" before the object name in the vcard files
# every object label may only contain letters, digits and the - character
# example:
# private_objects = Jabber, Skype, Twitter
private_objects = Jabber, Skype, Twitter
# preferred vcard version: 3.0 / 4.0
preferred_version = 3.0
# Look into source vcf files to speed up search queries: yes / no
search_in_source_files = no
# skip unparsable vcard files: yes / no
skip_unparsable = no

View file

@ -0,0 +1,24 @@
#!/bin/sh
if [ "$TERM" = "linux" ]; then
/bin/echo -e "
\e]P048483e
\e]P1dc2566
\e]P28fc029
\e]P3d4c96e
\e]P455bcce
\e]P59358fe
\e]P656b7a5
\e]P7acada1
\e]P876715e
\e]P9fa2772
\e]PAa7e22e
\e]PBe7db75
\e]PC66d9ee
\e]PDae82ff
\e]PE66efd5
\e]PFcfd0c2
"
# get rid of artifacts
# clear
fi

View file

@ -0,0 +1,10 @@
#! /usr/bin/env python2
from subprocess import check_output
def get_pass(account):
return check_output("pass " + account, shell=True).splitlines()[0]
def beep():
check_output("play -n synth sine E4 sine A5 remix 1-2 fade 0.5 1.2 0.5 2", shell=True)

View file

@ -0,0 +1,5 @@
plugins:
- mergePaths : false
- convertTransform : false
- cleanupNumericValues : false

View file

@ -0,0 +1,11 @@
[bar/base]
monitor = ${env:display}
bottom = true
[bar/primary]
inherit = bar/base
modules-right = date
[bar/secondary]
inherit = bar/base
modules-right = date

View file

@ -0,0 +1,18 @@
#!/usr/bin/env bash
if [ -f /proc/acpi/bbswitch ]
then
echo -n 
state="$(grep -o '\w\+$' /proc/acpi/bbswitch)"
if [ "$state" == "ON" ]
then
echo ""
elif [ "$state" == "OFF" ]
then
echo ""
else
echo "?"
fi
else
echo
fi

View file

@ -0,0 +1,390 @@
;=====================================================
;
; To learn more about how to configure Polybar
; go to https://github.com/jaagr/polybar
;
; The README contains alot of information
;
;=====================================================
[theme]
foreground = #f1ebeb
background = #272822
blackB = #48483e
blackF = #76715e
redB = #dc2566
redF = #fa2772
greenB = #8fc029
greenF = #a7e22e
yellowB = #d4c96e
yellowF = #e7db75
blueB = #55bcce
blueF = #66d9ee
magentaB = #9358fe
magentaF = #ae82ff
cyanB = #56b7a5
cyanF = #66efd5
whiteB = #acada1
whiteF = #cfd0c2
[colors]
background = ${theme.blackB}
foreground = ${theme.foreground}
[bar/base]
monitor = ${env:display:LVDS1}
width = 100%
height = 20
fixed-center = false
background = ${colors.background}
foreground = ${colors.foreground}
line-size = 0
border-size = 0
border-color = ${colors.background}
padding-left = 2
padding-right = 2
module-margin-left = 1
module-margin-right = 1
font-0 = "DejaVu Sans:size=10;0"
font-1 = "DejaVuSansMono Nerd Font Mono:pixelsize=10;0"
modules-left = i3
;wm-restack = bspwm
wm-restack = i3
;override-redirect = true
;scroll-up = bspwm-desknext
;scroll-down = bspwm-deskprev
;scroll-up = i3wm-wsnext
;scroll-down = i3wm-wsprev
bottom = true
enable-ipc = true
[bar/primary]
inherit = bar/base
modules-center = mpd
; modules-right = cpu memory temperature mail todo vpncheck eth wlan bbswitch xbacklight volume battery shortdate
modules-right = cpu memory temperature mail vpncheck eth wlan bbswitch xbacklight volume battery shortdate
tray-position = right
tray-padding = 2
tray-transparent = false
[bar/secondary]
inherit = bar/base
modules-right = cpu memory temperature keystore vpncheck ethMore wlanMore filesystem linuxmismatch bbswitch xbacklight volume date
[module/filesystem]
type = internal/fs
interval = 25
mount-0 = /
mount-1 = /home
label-mounted-foreground = ${theme.magentaB}
label-mounted = %{F#ae81ff}%mountpoint%%{F-} %free%
label-unmounted =
[module/i3]
type = internal/i3
pin-workspaces = true
strip-wsnumbers = false
index-sort = true
enable-click = true
enable-scroll = true
wrapping-scroll = false
reverse-scroll = true
fuzzy-match = false
ws-icon-0 = "1;"
ws-icon-1 = "2;"
ws-icon-2 = "3;"
ws-icon-3 = "4;"
ws-icon-4 = "5;▲"
ws-icon-5 = "6;▲"
ws-icon-6 = "7;"
ws-icon-7 = "8;"
ws-icon-8 = "9;"
ws-icon-9 = "10;"
ws-icon-default = ?
format = <label-state> <label-mode>
label-mode = %mode%
label-mode-padding = 2
label-mode-background = ${theme.redB}
label-focused = %index% %icon%
label-focused-foreground = ${theme.background}
label-focused-background = ${theme.greenB}
label-focused-padding = 2
label-unfocused = ${self.label-focused}
label-unfocused-padding = ${self.label-focused-padding}
label-visible = ${self.label-focused}
label-visible-padding = ${self.label-focused-padding}
label-visible-foreground = ${theme.greenF}
label-urgent = ${self.label-focused}
label-urgent-padding = ${self.label-focused-padding}
label-urgent-foreground = ${theme.foreground}
label-urgent-background = ${theme.redB}
[module/mpd]
type = internal/mpd
; format-online = <icon-prev> <icon-seekb> <icon-stop> <toggle> <icon-seekf> <icon-next> <icon-repeat> <icon-random> <bar-progress> <label-time> <label-song>
format-online = <icon-prev> <icon-seekb> <icon-stop> <toggle> <icon-seekf> <icon-next> <icon-repeat> <icon-random> <label-time> <label-song>
format-playing = ${self.format-online}
format-paused = ${self.format-online}
format-stopped = <toggle>
label-song =  %title% - %artist%
label-song-maxlen = 35
label-song-ellipsis = true
label-time = %elapsed%/%total%
label-offline =
icon-play = 
icon-pause = 
icon-stop = 
icon-prev = 
icon-next = 
icon-seekb = 
icon-seekf = 
icon-random = 
icon-repeat = 
icon-repeatone = 1
toggle-on-foreground = ${theme.foreground}
toggle-off-foreground = #55
[module/bbswitch]
type = custom/script
exec = ~/.config/polybar/bbswitch
interval = 5
format-foreground = ${theme.redF}
[module/todo]
type = custom/script
exec = ~/.config/polybar/todo
interval = 30
format-prefix = 
format-foreground = ${theme.yellowF}
[module/mail]
type = custom/script
exec = cat ~/.cache/mutt/status
interval = 1
format-prefix = 
format-foreground = ${theme.magentaF}
; format-background = ${theme.magentaB}
[module/linuxmismatch]
type = custom/script
exec = echo 
exec-if = ~/.config/polybar/linuxmismatch
interval = 30
format-foreground = ${theme.yellowF}
; format-background = ${theme.yellowF}
[module/xbacklight]
type = internal/xbacklight
output = ${env:display:LVDS1}
enable-scroll = true
format = <ramp>
ramp-0 = 🌕
ramp-1 = 🌔
ramp-2 = 🌓
ramp-3 = 🌒
ramp-4 = 🌑
format-foreground = ${theme.redF}
[module/backlight-acpi]
inherit = module/xbacklight
type = internal/backlight
card = intel_backlight
[module/cpu]
type = internal/cpu
interval = 1
format = <ramp-coreload>
format-foreground = ${theme.redF}
ramp-coreload-0 = ▁
ramp-coreload-1 = ▂
ramp-coreload-2 = ▃
ramp-coreload-3 = ▄
ramp-coreload-4 = ▅
ramp-coreload-5 = ▆
ramp-coreload-6 = ▇
ramp-coreload-7 = █
[module/memory]
type = internal/memory
interval = 1
format-foreground = ${theme.greenF}
label =  %gb_free%
[module/keystore]
type = custom/script
exec = ~/.config/polybar/keystore
; exec-if = pgrep openvpn
interval = 5
format-foreground = ${theme.greenF}
[module/vpncheck]
type = custom/script
exec = echo 
exec-if = pgrep openvpn
interval = 5
format-foreground = ${theme.blueF}
[module/eth]
type = internal/network
interface = ${env:ethI:eth0}
interval = 5
format-connected =  <label-connected>
label-connected = %local_ip%
format-connected-foreground = ${theme.blueF}
format-disconnected =
[module/ethMore]
inherit = module/eth
label-connected = ↑%upspeed% ↓%downspeed%
[module/wlan]
type = internal/network
interface = ${env:wlanI:wlan0}
interval = 5
format-connected = <ramp-signal> <label-connected>
label-connected = %local_ip% %essid%
format-connected-foreground = ${theme.cyanF}
format-disconnected =
ramp-signal-0 = ▁
ramp-signal-1 = ▂
ramp-signal-2 = ▃
ramp-signal-3 = ▄
ramp-signal-4 = ▅
ramp-signal-5 = ▆
ramp-signal-6 = ▇
ramp-signal-7 = █
animation-packetloss-0 = 
animation-packetloss-1 = 
animation-packetloss-framerate = 500
[module/wlanMore]
inherit = module/wlan
label-connected = ↑%upspeed% ↓%downspeed%
[module/date]
type = internal/date
interval = 1
date = " %d/%m/%Y"
time =  %H:%M:%S
format = <label>
format-foreground = ${theme.cyanF}
label = %date% %time%
[module/shortdate]
inherit = module/date
date = " %d/%m"
[module/volume]
type = internal/volume
master-soundcard = default
format-volume = <ramp-volume> <label-volume>
format-volume-foreground = ${theme.greenF}
label-muted = 
label-muted-foreground = #66
format-muted-foreground = ${self.format-volume-foreground}
ramp-volume-0 = 
ramp-volume-1 = 
ramp-volume-2 = 
[module/battery]
type = internal/battery
battery = BAT0
adapter = ADP0
full-at = 100
time-format = %H:%M
label-full = 
format-charging = <ramp-capacity> <label-charging>
format-charging-foreground = ${theme.yellowF}
format-charging-prefix = 
label-charging = %percentage%% (%time%)
format-discharging = <ramp-capacity> <label-discharging>
format-discharging-foreground = ${self.format-charging-foreground}
; format-discharging-background = ${theme.redB}
label-discharging = %percentage%% (%time%)
format-full-prefix = " "
format-full-foreground = ${self.format-charging-foreground}
ramp-capacity-0 = 
ramp-capacity-1 = 
ramp-capacity-2 = 
ramp-capacity-3 = 
ramp-capacity-4 = 
[module/temperature]
type = internal/temperature
thermal-zone = 2
warn-temperature = 70
format = <ramp> <label>
format-foreground = ${theme.yellowB}
label = %temperature%
format-warn = <ramp> <label-warn>
format-warn-foreground = ${self.format-foreground}
format-warn-background = ${theme.redB}
label-warn = %temperature%
ramp-0 = 
ramp-1 = 
ramp-2 = 
ramp-3 = 
[settings]
screenchange-reload = true
;compositing-background = xor
;compositing-background = screen
;compositing-foreground = source
;compositing-border = over
[global/wm]
margin-top = 5
margin-bottom = 5
; vim:ft=dosini

View file

@ -0,0 +1,2 @@
include-file = modules.ini
include-file = bars.ini

View file

@ -0,0 +1,9 @@
#!/usr/bin/env bash
output="$(ssh-add -l)"
if [ $? -ne 0 ]; then
echo
else
echo -n " "
echo "$output" | wc -l
fi

View file

@ -0,0 +1,28 @@
#!/usr/bin/env sh
# Terminate already running bar instances
killall -q polybar
# Wait until the processes have been shut down
while pgrep -x polybar >/dev/null; do sleep 1; done
# Getting some vars
primary=$(xrandr | grep primary | head -1 | cut -d' ' -f1)
export ethI=$(/bin/ls /sys/class/net/ | grep ^enp* | head -1)
export wlanI=$(/bin/ls /sys/class/net/ | grep ^wl* | head -1)
# Launch bar for each display
polybar -m | cut -d':' -f1 | while read display
do
export display=$display
if [ "$display" == "$primary" ]
then
bar="primary"
else
bar="secondary"
fi
polybar -q $bar -c ~/.config/polybar/config.ini &
done
echo "Bars launched..."

View file

@ -0,0 +1,17 @@
#!/usr/bin/env bash
if ! which pacman &> /dev/null
then
exit 1
fi
packageVersion=$(pacman -Q linux | cut -d' ' -f2)
runningVersion=$(uname -r)
if echo "$runningVersion" | grep "^$packageVersion" &> /dev/null
then
exit 1
else
exit 0
fi

View file

@ -0,0 +1,3 @@
[module/date]
type = internal/date
date = %m-%d %H:%M:%S

21
unprocessed/config/polybar/todo Executable file
View file

@ -0,0 +1,21 @@
#!/usr/bin/env bash
CALDIR="$HOME/.vdirsyncer/currentCalendars"
function status() {
ls "$CALDIR" | while read account
do
displayname="$(cat "$CALDIR/$account/displayname")"
color="$(cat "$CALDIR/$account/color")"
nb="$(todo list "$displayname" | grep -v "^$" | wc -l)"
if [ $nb -gt 0 ]
then
echo -n " %{F$color}$nb%{F-}"
fi
done
# Newline to tell polybar to update in any case
echo
}
status

View file

@ -0,0 +1,6 @@
#!/usr/bin/env bash
# Make sur eto install zlib-flate from the qpdf package
# Works on unencrypted only
tail -c +25 "$1" | zlib-flate -uncompress | tar xvf -

View file

@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -euxo pipefail
# Set variables
name="${1:-linux}"
kernel="/boot/vmlinuz-$name"
initrd="/boot/initramfs-$name.img"
# Test if required files are present
[ -f $kernel ]
[ -f $initrd ]
command -v kexec &> /dev/null
# Configure the next kernel to load
sudo kexec -l $kernel --initrd=$initrd --reuse-cmdline
# Gracefully restart on the next kernel
sudo systemctl kexec

View file

@ -0,0 +1,12 @@
#!/usr/bin/env bash
if [ -z $DISPLAY ]
then
sudo tee /proc/acpi/bbswitch <<< ON
"$@"
rmmod nvidia_uvm
rmmod nvidia
sudo tee /proc/acpi/bbswitch <<< OFF
else
PATH="/opt/cuda/bin:$PATH" LD_LIBRARY_PATH="/opt/cuda/lib64:$LD_LIBRARY_PATH" VBLANK=0 VGL_READBACK=pbo optirun -c yuv "$@"
fi

View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
# Downloads a font from dafont.com and
# extracts it in the user's font dir
wget "http://dl.dafont.com/dl/?f=$1" -O /tmp/dafont.zip
unzip /tmp/dafont.zip -d ~/.local/share/fonts -x *.txt
rm -rf /tmp/dafont.zip

455
unprocessed/config/scripts/debloc Executable file
View file

@ -0,0 +1,455 @@
#!/usr/bin/env bash
# Installs Debian packages on a Debian system
# with no root access, in the user home
# CONFIGURATION
# Verifications
if [[ -z $DEBIAN_MIRROR && ! -f /etc/apt/sources.list ]]; then
echo "Unable to find a mirror. Try setting DEBIAN_MIRROR (see help)."
exit 1
fi
if [[ -z $DEBIAN_DB && ! $(which apt &> /dev/null) ]]; then
echo "Unable to find a database for packages to install. Try setting DEBIAN_DB (see help)."
exit 1
fi
# Overrides
[ -z $DEBLOC_PREFIX ] && DEBLOC_PREFIX=$(dpkg --print-architecture)
[ -z $DEBLOC_DB ] && DEBLOC_DB=${XDG_CONFIG_HOME:-$HOME/.config}/debloc/$DEBLOC_PREFIX
[ -z $DEBLOC_ROOT ] && DEBLOC_ROOT=$HOME/.debloc/$DEBLOC_PREFIX
DEBLOC_LD=$DEBLOC_ROOT/ld
if [ -z "$DEBIAN_MIRROR" ]; then
DEBIAN_MIRROR="$(cat /etc/apt/sources.list | grep '^deb ' | grep main | grep -v backports)"
DEBIAN_MIRROR="$(echo -e "$DEBIAN_MIRROR" | cut -d ' ' -f 2 | sed 's/\/$//' | sort | uniq)"
fi
# Preparation
mkdir -p $DEBLOC_DB &> /dev/null
mkdir -p $DEBLOC_ROOT &> /dev/null
# PRIVATE FUNCTIONS
# Tell if a package exists
function _debloc-exists { # package
if [[ -n $DEBIAN_DB && -f $DEBIAN_DB ]]; then
grep "^Package: $1\$" $DEBIAN_DB --quiet
else
LANG=C apt-cache show $1 &> /dev/null
fi
if [ $? == 0 ]; then
return 1
else
return 0
fi
}
# Return the real package associated with a virtual package
# If not a virtual package, return the input
function _debloc-filterVirtual { # package
pkg=$1
if [[ -n $DEBIAN_DB && -f $DEBIAN_DB ]]; then
echo $pkg
else
LANG=C apt-cache policy $1 | grep "Candidate" | grep "(none)" > /dev/null
if [ $? == 0 ]; then
# TODO This is not really accurate
LANG=C apt-cache showpkg $pkg | tail -1 | cut -d ' ' -f 1
else
echo $pkg
fi
fi
return 0
}
# Tell if a package is installed via debloc
function _debloc-locallyInstalled { # package
if [ -f $DEBLOC_DB/$1 ]; then
return 1
else
return 0
fi
}
# Tell if a package is installed system-wide
function _debloc-globallyInstalled { # package
STATUS=$(mktemp)
LANG=C dpkg --list $1 &> $STATUS
if [ $? != 0 ]; then
rm -f $STATUS > /dev/null
return 0
fi
cat $STATUS | grep '^Status:' | grep ' installed' --quiet
if [ $? != 0 ]; then
rm -f $STATUS > /dev/null
return 0
else
rm -f $STATUS > /dev/null
return 1
fi
}
# Get informations about a package
function _debloc-packageShow { # package
pkg=$1
if [[ -n $DEBIAN_DB && -f $DEBIAN_DB ]]; then
startline=$(grep "^Package: ${pkg}\$" $DEBIAN_DB --line-number | tail -1 | cut -d ':' -f 1)
if [ -z "$startline" ]; then
return 0
fi
sed -n "$startline,$(expr $startline + 100)p" $DEBIAN_DB | while read line; do
if [ -z "$line" ]; then
return 0
fi
echo $line
done
return 1
else
LANG=C apt-cache show $pkg | while read line; do
if [ -z "$line" ]; then
return 0
fi
echo "$line"
done
return 0
fi
}
# Get the path of a package
function _debloc-packagePath { # package
_debloc-packageShow $1 | grep "^Filename:" | head -1 | cut -d ':' -f 2 | sed -e 's/^[[:space:]]*//'
return 0
}
# Get the md5sum of a package
function _debloc-packageMd5sum { # package
_debloc-packageShow $1 | grep "^MD5sum:" | cut -d ':' -f 2 | sed -e 's/^[[:space:]]*//'
return 0
}
# Update symbolics links in $DEBLOC_ROOT/lib
function _debloc-ldconfig {
mkdir -p $DEBLOC_LD &> /dev/null
rm -f $DEBLOC_LD &> /dev/null
find $DEBLOC_ROOT{/usr,}/lib -type f -name "*.so*" | while read lib; do
ln --symbolic --force "$lib" "$DEBLOC_LD/$(basename $lib)"
done &> /dev/null
find $DEBLOC_ROOT{/usr,}/lib -type l -name "*.so*" | while read link; do
yes | cp --force --no-dereference --preserve=links "$link" "$DEBLOC_LD" &> /dev/null
done &> /dev/null
}
# Fix absolute symbolic links
function _debloc-fixRootSymlinks {
find $DEBLOC_ROOT -type l | while read src
do
dst="$(readlink "$src")"
if echo "$dst" | grep '^/' | grep -q -v "^$DEBLOC_ROOT"
then
newDst="$DEBLOC_ROOT$dst"
if [ -f "$newDst" ]
then
echo "$src → $newDst"
rm "$src"
ln -s "$newDst" "$src"
else
echo "Ignoring $src pointing to $dst"
fi
fi
done
}
function _debloc-fixPkgconfPrefix {
sed "s|^prefix=/usr$|prefix=$DEBLOC_ROOT/usr|" $(find $DEBLOC_ROOT -type f -name "*.pc") -i
}
function debloc_fix {
echo "Fixing absolute symbolic links..."
_debloc-fixRootSymlinks
echo "Linking libraries in /ld"
_debloc-ldconfig
echo "Fixing prefix in pkg-config files"
_debloc-fixPkgconfPrefix
}
# Install debian archive
function _debloc-installDeb { # path
TMP_DIR=$(mktemp -d) &> /dev/null
$(cd $TMP_DIR; ar x "$1")
TAR_FILE=$(find $TMP_DIR -type f -name "data.tar.*" | head -1)
if [ -e "$TAR_FILE" ]; then
# Output for DB saving
tar tf $TAR_FILE
tar xf $TAR_FILE -C $DEBLOC_ROOT
# _debloc-ldconfig
mkdir -p $DEBLOC_LD &> /dev/null
tar tf $TAR_FILE | grep '^.\(/usr\)\?/lib/' | grep '\.so' | while read file; do
lib=$(readlink -f $DEBLOC_ROOT/$file)
if [ -f $lib ]; then
ln --symbolic --force "$lib" "$DEBLOC_LD/$(basename $file)"
fi
if [ -h $lib ]; then
yes | cp --force --no-dereference --preserve=links "$(basename $link)" "$DEBLOC_LD/" &> /dev/null
fi
done
fi
rm -rf $TMP_DIR &> /dev/null
return 0
}
# Install package
function _debloc-install { # package
pkg=$1
DEB_FILE=$(mktemp) &> /dev/null
path=$(_debloc-packagePath $pkg)
echo -e "${DEBIAN_MIRROR}" | while read mirror; do
if [ -z "$mirror" ]; then
continue
fi
url=${mirror}/${path}
echo "→ Downloading $url"
wget "$url" --quiet -O $DEB_FILE
if [ $? == 0 ]; then
break
fi
done
if [ ! -s $DEB_FILE ]; then
echo "→ Failed (no deb file)!"
rm $DEBLOC_DB/$pkg &> /dev/null
return 4
fi
echo "→ Verifying sums"
theo=$(_debloc-packageMd5sum $pkg)
real=$(md5sum $DEB_FILE | cut -d ' ' -f 1)
if [ "$theo" != "$real" ]; then
rm -f $DEB_FILE &> /dev/null
echo "→ Failed (sum doesn't match)!"
rm $DEBLOC_DB/$pkg &> /dev/null
return 5
fi
echo "→ Installing"
_debloc-installDeb $DEB_FILE > $DEBLOC_DB/$pkg
echo "→ Done!"
rm -f $DEB_FILE &> /dev/null
return 0
}
# Get the dependencies of a package
function _debloc-packageDeps { # package
_debloc-packageShow $1 | grep '^Depends:' | sed 's/Depends: //' | sed 's/, /\n/g' | cut -d ' ' -f 1
return 0
}
# Install package with dependencies
function _debloc-installDeps { # package
pkg=$1
echo "Installing $pkg"
touch $DEBLOC_DB/$pkg # To prevent cyclic deps
_debloc-packageDeps $pkg | while read dep; do
dep=$(_debloc-filterVirtual $dep)
_debloc-locallyInstalled $dep
if [ $? == 1 ]; then
echo "- Dependency $dep is already installed with Debloc"
continue
fi
_debloc-globallyInstalled $dep
if [ $? == 1 ]; then
echo "- Dependency $dep is already installed on the system"
continue
fi
_debloc-installDeps $dep | while read line; do echo "- $line"; done
done
_debloc-install $pkg
return 0
}
# PUBLIC FUNCTIONS
function proxy_set_help {
echo "Usage: $0 env"
echo
echo "Examples:"
echo ' eval "$(debloc env)"'
return 0
}
function debloc_env {
echo "export PATH=\"$DEBLOC_ROOT/usr/bin:$DEBLOC_ROOT/usr/games/:$DEBLOC_ROOT/usr/lib/git-core:\$PATH\""
echo "export LIBRARY_PATH=\"$DEBLOC_LD:\$LIBRARY_PATH\""
echo "export C_INCLUDE_PATH=\"$DEBLOC_ROOT/usr/include:\$C_INCLUDE_PATH\""
echo "export CPLUS_INCLUDE_PATH=\"$DEBLOC_ROOT/usr/include:$DEBLOC_ROOT/usr/include/python2.7/:$DEBLOC_ROOT/usr/include/x86_64-linux-gnu/python2.7/:\$CPLUS_INCLUDE_PATH\""
echo "export LD_LIBRARY_PATH=\"$DEBLOC_LD:\$LD_LIBRARY_PATH\""
echo "export PYTHONPATH=\"$DEBLOC_ROOT/usr/lib/python2/dist-packages:$DEBLOC_ROOT/usr/lib/python3/dist-packages:$DEBLOC_ROOT/usr/lib/python2.7/dist-packages:$DEBLOC_ROOT/usr/lib/python3.5/dist-packages:\$PYTHONPATH\""
echo "export QT_QPA_PLATFORM_PLUGIN_PATH=\"$DEBLOC_ROOT/usr/lib/x86_64-linux-gnu/qt5/plugins/platforms\""
echo "export PKG_CONFIG_PATH=\"$DEBLOC_ROOT/usr/share/pkgconfig/:$DEBLOC_ROOT/usr/lib/x86_64-linux-gnu/pkgconfig/:$DEBLOC_ROOT/usr/lib/pkgconfig/:\$PKG_CONFIG_PATH\""
}
function debloc_info {
echo "DEBLOC_PREFIX=$DEBLOC_PREFIX"
echo "DEBLOC_ROOT=$DEBLOC_ROOT"
echo "DEBLOC_DB=$DEBLOC_DB"
echo "DEBLOC_LD=$DEBLOC_LD"
echo "DEBIAN_MIRROR='$DEBIAN_MIRROR'"
echo "DEBIAN_DB=$DEBIAN_DB"
}
function debloc_install_help {
echo "Usage: $0 install PACKAGE"
echo
echo "Arguments:"
echo " PACKAGE Package name"
return 0
}
function debloc_install { # package
if [ -z $1 ]; then
debloc_deb_help
fi
for pkg in $*
do
if [ $pkg == '--force' ] || [ $pkg == '-f' ]; then
force=0
fi
done
for pkg in $*; do
if [ $pkg == '--force' ] || [ $pkg == '-f' ]; then
continue
fi
pkg=$(_debloc-filterVirtual $pkg)
_debloc-exists $pkg
if [ $? == 0 ]; then
echo "Unknown package $pkg"
continue
fi
if [ ! -v force ]; then
_debloc-locallyInstalled $pkg
if [ $? == 1 ]; then
echo "Package $pkg is already installed with Debloc"
continue
fi
_debloc-globallyInstalled $pkg
if [ $? == 1 ]; then
echo "Package $pkg is already installed on the system"
continue
fi
fi
_debloc-installDeps $pkg
done
return 0
}
function debloc_deb_help {
echo "Usage: $0 deb PATH"
echo
echo "Arguments:"
echo " PATH Path to the .deb file"
return 0
}
function debloc_deb { # path
if [ -z $1 ]; then
debloc_deb_help
fi
for path in $*; do
if [ ! -f "$path" ]; then
echo "$path is not a file"
return 6
fi
echo "Installing $(basename $path)"
_debloc-installDeb "$(readlink -f $path)" > $DEBLOC_DB/$(basename $path)
done
return 0
}
function debloc_altern_help {
echo "Usage: $0 altern PROGRAM ALTERNATIVE"
echo
echo "Arguments:"
echo " PROGRAM Program to set the alternative for"
echo " ALTERNATIVE Alternative to set"
echo
echo "Examples:"
echo " $0 altern vim nox"
echo " $0 altern dmenu xft"
return 0
}
function debloc_altern { # program alternative
if [[ -z $1 || -z $2 ]]; then
debloc_altern_help
exit 1
fi
if [ -f "$DEBLOC_ROOT/usr/bin/$1.$2" ]; then
dest="$DEBLOC_ROOT/usr/bin/$1"
alte="$DEBLOC_ROOT/usr/bin/$1.$2"
elif [ -f "$DEBLOC_ROOT/bin/$1.$2" ]; then
dest="$DEBLOC_ROOT/bin/$1"
alte="$DEBLOC_ROOT/bin/$1.$2"
else
echo "Unknown alternative for $1 : $2"
exit 1
fi
if [ -e "$dest" ]; then
rm $dest
fi
ln -s "$alte" "$dest"
}
function debloc_flush {
rm -rf $DEBLOC_ROOT/* &> /dev/null
rm -f $DEBLOC_DB/* &> /dev/null
}
# TODO Other word for 'fake filesystem' and/or explain what this is
function debloc_help {
command="$1"
if [ -n "$command" ]; then
if type "debloc_${command}_help" &> /dev/null; then
shift
"debloc_${command}_help" "$@"
return $?
fi
fi
echo "Usage: $0 COMMAND"
echo
echo "Commands:"
echo " env Provides the environment variables required to run applications from the fake filesystem"
echo " info Gives some information about the fake filesystem"
echo " install Install a debian package in the fake filesystem"
echo " deb Install from a .deb file in the fake filesystem"
echo " altern Update alternative"
echo " fix Apply some fixes in the fake filesystem"
echo " flush Remove every package installed from the fake filesystem"
echo " help Get help with commands"
echo
echo "Environment variables:"
echo " DEBLOC_PREFIX Name of the fake filesystem to use (default: uses dpkg architecture)"
echo " DEBLOC_ROOT Path of the fake filesystem (default: ~/.debloc/\$DEBLOC_PREFIX/)"
echo " DEBLOC_DB Database of the fake filesystem (default: \$XDG_CONFIG_HOME/debloc/\$DEBLOC_PREFIX)"
echo " DEBIAN_MIRROR Multiline list of debian mirror (default: uses /etc/apt/sources.list)"
echo " DEBIAN_DB Path to a file with all packages description (default: uses apt-cache showpkg)"
echo " help Get help with commands"
return 0
}
# MAIN
command="$1"
shift
if type "debloc_$command" &> /dev/null; then
"debloc_$command" "$@"
else
debloc_help
fi

View file

@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Don't forget:
# --duration 15m # Specify expected duration of presentation
# --half-screen --geometry 2048x768+0+1 # If presenting with beamer notes on the right, and you have two screens with 1024x768 resolution
/home/geoffrey/Documents/Programmation/Impressive/OutOfTree/impressive.py \
--transition WipeRight \
--bind lmb:=box-zoom --bind lmb=zoom-exit \
--bind rmb:=box-add --bind rmb=box-clear \
--bind ctrl+p:=overview-enter --bind ctrl+a:=overview-confirm \
--bind escape:=time-reset \
--bind e:=goto-last \
--bind b-=fade-to-black \
--bind escape-=quit \
--cursor default \
--fontsize 26 \
--transtime 200 \
--mousedelay 1000 \
--page-progress \
--time-display \
--tracking \
--zoomdarkness 75 \
"$@"

2
unprocessed/config/scripts/hc Executable file
View file

@ -0,0 +1,2 @@
#!/usr/bin/env bash
highlight -O ansi "$@"

2
unprocessed/config/scripts/hl Executable file
View file

@ -0,0 +1,2 @@
#!/usr/bin/env bash
highlight -O ansi "$@" | less -R

View file

@ -0,0 +1,56 @@
#!/usr/bin/env node
// Imports
var fs = require('fs');
var pdf = require('html-pdf');
var yargs = require('yargs');
// Understanding
var argv = yargs
.usage("Usage: $0 -o out.pdf [options]")
.example('$0 -i doc.pdf -o doc.pdf', 'Convert doc.html to PDF using the default values')
.help('h')
.alias('h', 'help')
.describe('i', 'Input file')
.alias('i', 'input')
.default('i', '/dev/stdin')
.describe('o', 'Output file')
.alias('o', 'output')
.describe('t', 'Title of file')
.alias('t', 'title')
.default('t', 'Sans titre')
.describe('b', 'Border')
.alias('b', 'border')
.default('b', '2cm')
.demandOption(['o'])
.argv;
// Settings
options = {
"base": "file://" + process.cwd() + '/',
"format": "A4",
"orientation": "portrait",
"border": argv.border,
"footer": {
"height": "10mm",
"contents": {
default: '<div style="text-align: left; float: left;">' + argv.title + '</div> <div style="text-align:right; float: right;">{{page}}/{{pages}}</div>',
}
},
}
// Reading
htmlString = fs.readFileSync(argv.i, "utf8");
// Conversion
pdf.create(htmlString, options).toFile(argv.o, function(err, res) {
if (err) console.error(err);
});

View file

@ -0,0 +1,11 @@
#!/usr/bin/env bash
# Setups a WSL system the way I like it
# Remember to use the alwsl script on GitHub to install Arch Linux on WSL
# Use github:cbucher/console as a console and github:mintty/wsltty as a shell
# (%LOCALAPPDATA%/wsltty/bin/wsl-bridge) to avoid arrow keys bypassing
(cd /usr/share/i18n/charmaps/; sudo gunzip -k UTF8.gz)
echo "fr_FR.UTF-8 UTF-8" | sudo tee -a /etc/locale.gen
sudo locale-gen

View file

@ -0,0 +1,6 @@
#!/usr/bin/env bash
serv="$1"
shift
ssh "$serv" 'sudo tail --follow=name --retry --quiet $(sudo find $(echo /var/log/$([ -d /var/log/httpd/ ] && echo httpd || echo apache2)) -type f -name *access.log)' | logstalgia --sync "$@"

View file

@ -0,0 +1,527 @@
#!/usr/bin/env bash
# Handles indexing and SSH keys of machines I
# have access on
MACHINES_HOME=$HOME
MACHINES_CONFIG=$HOME/.config/machines
MACHINES_API=https://machines.frogeye.fr
mkdir -p "$MACHINES_HOME" &> /dev/null
mkdir -p "$MACHINES_CONFIG" &> /dev/null
# COMMON
function prompt { # text
while true
do
read -r -p "$1 [yn] " yn
case $yn in
[Yy]* ) return 1;;
[Nn]* ) return 0;;
* ) echo "Please answer y or n.";;
esac
done
}
# From https://gist.github.com/cdown/1163649
urlencode() { # string
old_lc_collate=$LC_COLLATE
LC_COLLATE=C
local length="${#1}"
for (( i = 0; i < length; i++ )); do
local c="${1:i:1}"
case $c in
[a-zA-Z0-9.~_-]) printf "%s" "$c" ;;
*) printf '%%%02X' "'$c" ;;
esac
done
LC_COLLATE=$old_lc_collate
}
urldecode() { # string
local url_encoded="${1//+/ }"
printf '%b' "${url_encoded//%/\\x}"
}
# API ACCESS
function _machines-api {
route=$1
shift
temp=$(mktemp)
wget "$MACHINES_API/$route" --content-on-error --quiet --output-document=$temp "$@"
result=$?
if [ $result != 0 ]; then
echo "[ERROR] wget returned $result for route $route" 1>&2;
cat $temp 1>&2;
rm $temp
exit 2
fi
cat $temp
rm $temp
}
function _machines-apiToken {
read -r -p 'TOTP token: ' token
_machines-api "$@" --header="X-TOTP: $token"
}
function _machines-apiSigned {
_machines-ensureAdmin
_machines-api "$@" --certificate="$MACHINES_CONFIG/machines.crt" --private-key="$MACHINES_CONFIG/machines.key"
}
# APPLICATION KEYS & CERTIFICATE
function _machines-pubFromCrt {
openssl x509 -in "$MACHINES_CONFIG/machines.crt" -pubkey -noout > "$MACHINES_CONFIG/machines.pub"
}
function _machines-verifyCertificate {
return
if openssl verify "$MACHINES_CONFIG/machines.crt" | grep -v 'error 18' | grep 'error' --quiet; then
echo "[ERROR] Invalid certificate" 1>&2;
exit 1
fi
}
function _machines-ensurePub {
if [ ! -f "$MACHINES_CONFIG/machines.crt" ]; then
CERT_FILE=$(mktemp)
echo "[INFO] Downloading certificate..."
_machines-api cert > "$CERT_FILE"
openssl x509 -fingerprint -in "$CERT_FILE" | grep Fingerprint --color=never
prompt "Is this correct ?"
if [ $? == 1 ]; then
mv "$CERT_FILE" "$MACHINES_CONFIG/machines.crt" &> /dev/null
else
echo "[ERROR] Certificate rejected." 1>&2;
exit 1
fi
fi
_machines-verifyCertificate
if [ ! -f "$MACHINES_CONFIG/machines.pub" ]; then
_machines-pubFromCrt
fi
return 0
}
function _machines-ensureAdmin {
if [ ! -f "$MACHINES_CONFIG/machines.key" ]; then
echo "[ERROR] You need have to have the private key to do that" 1>&2;
exit 1
fi
}
# SSH ACCESS KEYS
function _machines-signAkey { # network
KEY_FILE=$(mktemp)
SIGN_FILE=$(mktemp)
_machines-apiSigned "akey/$1?unsigned" > "$KEY_FILE"
openssl dgst -sha256 -sign "$MACHINES_CONFIG/machines.key" -out "$SIGN_FILE" "$KEY_FILE"
_machines-apiSigned "akey/$1" --method=PUT --body-file="$SIGN_FILE"
rm "$KEY_FILE" "$SIGN_FILE" &> /dev/null
}
function _machines-getAkey { # network
_machines-ensurePub
KEY_FILE=$(mktemp)
SIGN_FILE=$(mktemp)
_machines-api "akey/$1" > "$KEY_FILE"
_machines-api "akey/$1?signature" > "$SIGN_FILE"
if openssl dgst -sha256 -verify "$MACHINES_CONFIG/machines.pub" -signature "$SIGN_FILE" "$KEY_FILE" &> /dev/null
then
cat "$KEY_FILE"
\rm "$KEY_FILE" "$SIGN_FILE"
return 0
else
\rm "$KEY_FILE" "$SIGN_FILE"
exit 1
fi
}
function _machines-updateAkey {
MYKEY_FILE=$(mktemp)
network=$(grep '^network=' "$MACHINES_CONFIG/this" | cut -d '=' -f 2)
if _machines-getAkey "$network" > "$MYKEY_FILE"
then
\mv -f "$MYKEY_FILE" "$MACHINES_HOME/.ssh/authorized_keys"
if [ -f "$MACHINES_HOME/.ssh/authorized_keys.tail" ]
then
cat "$MACHINES_HOME/.ssh/authorized_keys.tail" >> "$MACHINES_HOME/.ssh/authorized_keys"
fi
return 0
else
cat "$MYKEY_FILE"
echo "[ERROR] Authorized keys are not properly signed" 1>&2;
\rm "$MYKEY_FILE"
exit 1
fi
}
function _machines-postFile { # filename
cat $1 | while read -r line; do
parameter=$(echo "$line" | cut -d '=' -f 1)
value="$(echo "$line" | sed 's/^[a-zA-Z0-9]\+\(\[\]\)\?=//')"
echo -n "&$parameter=$(urlencode "$value")"
done
}
function _machines-addElement { # element elementType default
FILE=$(mktemp)
echo -e "$3" > "$FILE"
$EDITOR "$FILE"
data=$(_machines-postFile "$FILE")
\rm "$FILE"
_machines-apiSigned "$2" --post-data "name=$1$data"
}
function _machines-viewElement { # element elementType
_machines-apiSigned "$2/$1"
}
function _machines-editElement { # element elementType
FILE=$(mktemp)
_machines-apiSigned "$2/$1" > "$FILE"
$EDITOR "$FILE"
data=$(_machines-postFile "$FILE")
rm "$FILE" &> /dev/null
err=$(_machines-apiSigned "$2/$1" --post-data "$data")
}
function _machines-deleteElement { # element elementType
err=$(_machines-apiSigned "$2/$1" --method=DELETE)
}
# USER ADMIN FUNCTIONS
function machines_history {
if [ -f "$MACHINES_CONFIG/lastVerifiedLog" ]; then
from=$(<"$MACHINES_CONFIG/lastVerifiedLog")
else
from=0
fi
d=$(date +%s)
_machines-apiSigned log?from=$from | less
if prompt "Is this OK?"
then
exit 1
else
echo "$d" > "$MACHINES_CONFIG/lastVerifiedLog"
return 0
fi
}
function machines_sign {
machines_history
echo "Signing default network authorized_keys..."
_machines-signAkey
_machines-apiSigned network | while read -r network; do
echo "Signing network $network authorized_keys..."
_machines-signAkey $network
done
}
function machines_machine_list {
_machines-apiSigned machine
}
function machines_network_list {
_machines-apiSigned network
}
function machines_machine_add_help {
echo "Usage: $0 machine|mac|m add MACHINE"
echo
echo "Arguments:"
echo " MACHINE machine to add"
return 0
}
function machines_machine_add { # machine
if [ -z "$1" ]; then
machines_machine_add_help
exit 1
fi
_machines-addElement "$1" machine "host[]=\nnetwork=\nuserkey=\nhostkey=\nuser="
}
function machines_network_add_help {
echo "Usage: $0 network|net|n add NETWORK"
echo
echo "Arguments:"
echo " NETWORK Network to add"
return 0
}
function machines_network_add { # network
if [ -z "$1" ]; then
machines_network_add_help
exit 1
fi
_machines-addElement "$1" network "allowed[]=\nsecure=false"
}
function machines_machine_view_help {
echo "Usage: $0 machine|mac|m view MACHINE"
echo
echo "Arguments:"
echo " MACHINE machine to view"
return 0
}
function machines_machine_view { # machine
if [ -z "$1" ]; then
machines_machine_view_help
exit 1
fi
_machines-viewElement "$1" machine
}
function machines_network_view_help {
echo "Usage: $0 network|net|n view NETWORK"
echo
echo "Arguments:"
echo " NETWORK Network to view"
return 0
}
function machines_network_view { # network
if [ -z "$1" ]; then
machines_network_view_help
exit 1
fi
_machines-viewElement "$1" network
}
function machines_machine_edit_help {
echo "Usage: $0 machine|mac|m edit MACHINE"
echo
echo "Arguments:"
echo " MACHINE machine to edit"
return 0
}
function machines_machine_edit { # machine
if [ -z "$1" ]; then
machines_machine_edit_help
exit 1
fi
_machines-editElement "$1" machine
}
function machines_network_edit_help {
echo "Usage: $0 network|net|n edit NETWORK"
echo
echo "Arguments:"
echo " NETWORK Network to edit"
return 0
}
function machines_network_edit { # network
if [ -z "$1" ]; then
machines_network_edit_help
exit 1
fi
_machines-editElement "$1" network
}
function machines_machine_delete_help {
echo "Usage: $0 machine|mac|m delete machine"
echo
echo "Arguments:"
echo " MACHINE machine to remove"
return 0
}
function machines_machine_delete { # machine
if [ -z "$1" ]; then
machines_machine_delete_help
exit 1
fi
_machines-deleteElement "$1" machine
}
function machines_network_delete_help {
echo "Usage: $0 network|net|n delete NETWORK"
echo
echo "Arguments:"
echo " NETWORK Network to remove"
return 0
}
function machines_network_delete { # network
if [ -z "$1" ]; then
machines_network_delete_help
exit 1
fi
_machines-deleteElement "$1" network
}
function machines_machine_help {
echo "Usage: $0 machine|mac|m COMMAND"
echo
echo "Commands:"
echo " list List all machines"
echo " add Interactively add a machine"
echo " view Display a machine"
echo " edit Interactively edit a specified machine"
echo " delete Remove a specified machine"
echo " help Get help with commands"
return 0
}
function machines_machine {
command="$1"
shift
if type "machines_machine_$command" &> /dev/null; then
"machines_machine_$command" "$@"
else
machines_machine_help
fi
}
function machines_network_help {
echo "Usage: $0 network|net|n COMMAND"
echo
echo "Commands:"
echo " list List all networks"
echo " add Interactively add a network"
echo " view Display a network"
echo " edit Interactively edit a specified network"
echo " delete Remove a specified network"
echo " help Get help with commands"
return 0
}
function machines_network {
command="$1"
shift
if type "machines_network_$command" &> /dev/null; then
"machines_network_$command" "$@"
else
machines_network_help
fi
}
machines_mac() { machines_machine "$@"; }
machines_m() { machines_machine "$@"; }
machines_net() { machines_network "$@"; }
machines_n() { machines_network "$@"; }
machines_mac_help() { machines_machine_help "$@"; }
machines_m_help() { machines_machine_help "$@"; }
machines_net_help() { machines_network_help "$@"; }
machines_n_help() { machines_network_help "$@"; }
function machines_update-all {
machines_machine_list | while read -r machine; do
echo "Updating $machine..."
if [ $machine = $(cat "$MACHINES_CONFIG/this.name") ]; then
machines_update
continue
fi
ssh -n "$machine" 'cd .dotfiles; git pull; ./config/scripts/machines update'
done
}
function machines_regen-keys {
if [[ -e $MACHINES_CONFIG/machines.key || -e $MACHINES_CONFIG/machines.pub || -e $MACHINES_CONFIG/machines.crt ]]; then
echo "[ERROR] Please delete the pem files manually to prove you know what you're doing." 1>&2;
exit 1
else
openssl genrsa -out "$MACHINES_CONFIG/machines.key" 4096
chmod 600 "$MACHINES_CONFIG/machines.key"
openssl req -key "$MACHINES_CONFIG/machines.key" -new -out "$MACHINES_CONFIG/machines.csr"
openssl x509 -req -days 1826 -in "$MACHINES_CONFIG/machines.csr" -signkey "$MACHINES_CONFIG/machines.key" -out "$MACHINES_CONFIG/machines.crt"
_machines-pubFromCrt
fi
}
# USER FUNCTIONS
function machines_setup {
if [ -e "$MACHINES_CONFIG/this.name" ]; then
echo "[ERROR] This machine is already set up" 1>&2;
exit 1
fi
_machines-ensurePub
# Variables
read -r -p 'Machine name? ' name
read -r -p 'Hosts (separated by spaces)? ' hosts
# User key
mkdir -p "$MACHINES_HOME/.ssh" &> /dev/null
if [[ ! -f $MACHINES_HOME/.ssh/id_rsa || ! -f $MACHINES_HOME/.ssh/id_rsa.pub ]]; then
ssh-keygen -b 4096 -C "$name@machines.frogeye.fr" -f "$MACHINES_HOME/.ssh/id_rsa" -t rsa
fi
userkey=$(<"$MACHINES_HOME/.ssh/id_rsa.pub")
# Host key
for type in ecdsa ed25519 rsa dsa; do
if [ -f "/etc/ssh/ssh_host_${type}_key.pub" ]; then
hostkey=$(<"/etc/ssh/ssh_host_${type}_key.pub")
break
fi
done
# Subscription
data="name=$(urlencode "$name")&userkey=$(urlencode "$userkey")&hostkey=$(urlencode "$hostkey")&user=$(urlencode "$USER")"
for host in $hosts; do
data="$data&host[]=$(urlencode "$host")"
done
_machines-apiToken machine --post-data "$data"
echo "$name" > "$MACHINES_CONFIG/this.name"
machines_update
}
function machines_update {
_machines-api "machine/$(cat "$MACHINES_CONFIG/this.name")" > "$MACHINES_CONFIG/this"
_machines-updateAkey
}
function machines_totp {
url=$(_machines-apiSigned totp)
echo "URL : $url"
echo "$url" | qrencode -o - | feh -
}
function machines_help {
command="$1"
if [ -n "$command" ]; then
if type "machines_${command}_help" &> /dev/null; then
shift
"machines_${command}_help" "$@"
return $?
fi
fi
echo "Usage: $0 COMMAND"
echo
echo "User commands:"
echo " setup Interactive initial setup for new machine"
echo " update Update this machine"
echo " help Get help with commands"
echo
echo "Admin commands:"
echo " machine|mac|m Modify machines"
echo " network|net|n Modify networks"
echo " update-all Update all machines available via SSH"
echo " regen-keys Regenerate system keys"
echo " sign Sign recent transactions for propagation"
echo " totp Get TOTP generating QR code / URL"
return 0
}
# MAIN
command="$1"
shift
if type "machines_$command" &> /dev/null; then
"machines_$command" "$@"
else
machines_help "$@"
fi

View file

@ -0,0 +1,128 @@
#!/usr/bin/env node
// Imports
var fs = require('fs');
var marked = require('marked');
var highlight = require('highlight.js');
var katex = require('katex');
var yargs = require('yargs');
var extend = require('util')._extend;
// Constants
var template = '<!DOCTYPE html> <html lang="fr"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta charset="UTF-8"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.11.0/styles/xcode.min.css" integrity="sha256-OED7Gmqde0cMVVeo1zVd+3fBD4EST32D4h9YT7KY0aY=" crossorigin="anonymous" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.7.1/katex.min.css" integrity="sha384-wITovz90syo1dJWVh32uuETPVEtGigN07tkttEqPv+uR2SE/mbQcG7ATL28aI9H0" crossorigin="anonymous"> <style type="text/css"> image { max-width: 100 % } </style> <title>%TITLE%</title> </head> <body> <main class="page-content" aria-label="Content"> %BODY% </main> </body> </html>'
// Understanding
var argv = yargs
.usage("Usage: $0 [options]")
.example('$0 -i doc.md -o doc.html', 'Convert doc.md to HTML using the default template')
.help('h')
.alias('h', 'help')
.describe('i', 'Input file')
.alias('i', 'input')
.default('i', '/dev/stdin')
.describe('o', 'Output file')
.alias('o', 'output')
.default('o', '/dev/stdout')
.describe('t', 'Template file (%BODY% is replaced by the text)')
.alias('t', 'template')
.argv;
if (argv.t) {
template = fs.readFileSync(argv.t, "utf8");
}
var latex = true;
// TODO Arg
// Settings
var extraLangages = {
avrpseudo: function(hljs) {
lang = extend({}, highlight.getLanguage('avrasm'));
lang.keywords.keyword += ' Si Alors Sinon FinSi TantQue FinTantQue Pour FinPour allant de à ←';
lang.keywords.keyword += ' Lire Sortir sur Appeler Retourner';
lang.keywords.keyword += ' DecalerDroite DecalerGauche';
lang.keywords.keyword += ' Incrementer Decrementer';
lang.keywords.built_in += ' vrai faux';
lang.contains.push({
className: 'meta',
begin: /Configurer.+/,
end: /\n/,
});
return lang;
},
avrasmplus: function(hljs) {
lang = extend({}, highlight.getLanguage('avrasm'));
lang.keywords.keyword += ' si saut alors et ou if then goto && || <-';
lang.contains.push({
className: 'meta',
begin: /@\w+/,
});
return lang;
},
};
for (lang in extraLangages) {
// This must be done before any call to highlight.highlight :/
highlight.registerLanguage(lang, extraLangages[lang]);
}
var renderer = new marked.Renderer();
marked.setOptions({
highlight: function(code, lang) {
if (lang == 'raw') {
return code;
} else if (highlight.getLanguage(lang)) {
return highlight.highlight(lang, code).value;
} else {
// if (extraLangages[lang]) {
// highlight.registerLanguage(lang, extraLangages[lang]);
// return highlight.highlight(lang, code).value;
// } else {
// }
console.warn("Unknown language: " + lang);
return highlight.highlightAuto(code).value;
}
}
});
// Processing
markdownString = fs.readFileSync(argv.i, "utf8");
// TeX
if (latex) {
markdownString = markdownString.replace(/\\\$/g, '&dollar;')
markdownString = markdownString.replace(/\$\$([\s\S]+)\$\$/gm, function(glob, formula) {
return katex.renderToString(formula, {
displayMode: true
});
});
markdownString = markdownString.replace(/\$([^$]+)\$/g, function(glob, formula) {
return katex.renderToString(formula, {
displayMode: false
});
});
}
// Conversion
htmlString = marked(markdownString, {
renderer: renderer,
breaks: false
});
// fullHtmlString = htmlString;
fullHtmlString = template.replace('%BODY%', () => { return htmlString });
// Saving
if (argv.o == '/dev/stdout') {
console.log(fullHtmlString);
} else {
fs.writeFileSync(argv.o, fullHtmlString);
}

961
unprocessed/config/scripts/mel Executable file
View file

@ -0,0 +1,961 @@
#!/usr/bin/env python3
# pylint: disable=E1101
"""
Meh mail client
A dumb Python scripts that leverages notmuch, mbsync, and msmtp
to become a fully-functional extremly-opinonated mail client.
"""
# TODO Features
# TODO Implement initial command set
# TODO Lockfiles for write operations on mail files (mbsync,
# tags→maildir operations)
# TODO OPTI Lockfile per account and process everything in parallel
# (if implemented, this should be optional since while it may speed up
# the mail fetching process, its multi-threading nature would cause a
# lot of cache flushes and be not very efficient on battery)
# TODO Handle true character width
# TODO IMAP IDLE watches?
# TODO GPG
# TODO (only then) Refactor
# TODO Merge file with melConf
# TODO Config system revamp
import argparse
import configparser
import datetime
import email.message
import email.parser
import html
import logging
import mailcap
import os
import pdb
import re
import shutil
import subprocess
import sys
import traceback
import typing
import colorama
import coloredlogs
import notmuch
import progressbar
import xdg.BaseDirectory
MailLocation = typing.NewType("MailLocation", typing.Tuple[str, str, str])
# MessageAction = typing.Callable[[notmuch.Message], None]
class MelEngine:
"""
Class with all the functions for manipulating the database / mails.
"""
def load_config(self, config_path: str) -> configparser.ConfigParser:
"""
Load the configuration file into MelEngine
"""
self.log.info("Loading config file: %s", config_path)
if not os.path.isfile(config_path):
self.log.fatal("Config file not found!")
sys.exit(1)
# TODO Create it, maybe?
config = configparser.ConfigParser()
config.read(config_path)
# NOTE An empty/inexistant file while give an empty config
return config
def generate_aliases(self) -> None:
"""
Populate MelEngine.aliases and MelEngine.accounts
"""
assert self.config
for name in self.config.sections():
if not name.islower():
continue
section = self.config[name]
self.aliases.add(section["from"])
if "alternatives" in section:
for alt in section["alternatives"].split(";"):
self.aliases.add(alt)
self.accounts[name] = section
def __init__(self, config_path: str) -> None:
self.log = logging.getLogger("MelEngine")
self.config = self.load_config(config_path)
self.database = None
# Caches
self.accounts: typing.Dict[str, configparser.SectionProxy] = dict()
# All the emails the user is represented as:
self.aliases: typing.Set[str] = set()
# TODO If the user send emails to himself, maybe that wont cut it.
self.generate_aliases()
def notmuch_new(self) -> None:
"""
Runs `notmuch new`, which basically update the database
to match the mail folder.
"""
assert not self.database
self.log.info("Indexing mails")
notmuch_config_file = os.path.expanduser(
"~/.config/notmuch-config"
) # TODO Better
cmd = ["notmuch", "--config", notmuch_config_file, "new"]
self.log.debug(" ".join(cmd))
subprocess.run(cmd, check=True)
def list_folders(self) -> typing.List[typing.Tuple[str, ...]]:
"""
List all the folders of the mail dir.
"""
assert self.config
storage_path = os.path.realpath(
os.path.expanduser(self.config["GENERAL"]["storage"])
)
folders = list()
for account in self.accounts:
storage_path_account = os.path.join(storage_path, account)
for root, dirs, _ in os.walk(storage_path_account):
if "cur" not in dirs or "new" not in dirs or "tmp" not in dirs:
continue
assert root.startswith(storage_path)
path = root[len(storage_path) :]
path_split = path.split("/")
if path_split[0] == "":
path_split = path_split[1:]
folders.append(tuple(path_split))
return folders
def open_database(self, write: bool = False) -> None:
"""
Open an access notmuch database in read or read+write mode.
Be sure to require only in the mode you want to avoid deadlocks.
"""
assert self.config
mode = (
notmuch.Database.MODE.READ_WRITE
if write
else notmuch.Database.MODE.READ_ONLY
)
if self.database:
# If the requested mode is the one already present,
# or we request read when it's already write, do nothing
if mode in (self.database.mode, notmuch.Database.MODE.READ_ONLY):
return
self.log.info("Current database not in mode %s, closing", mode)
self.close_database()
self.log.info("Opening database in mode %s", mode)
db_path = os.path.realpath(
os.path.expanduser(self.config["GENERAL"]["storage"])
)
self.database = notmuch.Database(mode=mode, path=db_path)
def close_database(self) -> None:
"""
Close the access notmuch database.
"""
if self.database:
self.log.info("Closing database")
self.database.close()
self.database = None
def get_location(self, msg: notmuch.Message) -> MailLocation:
"""
Return the filesystem location (relative to the mail directory)
of the given message.
"""
path = msg.get_filename()
path = os.path.dirname(path)
assert self.database
base = self.database.get_path()
assert path.startswith(base)
path = path[len(base) :]
path_split = path.split("/")
mailbox = path_split[1]
assert mailbox in self.accounts
state = path_split[-1]
folder = tuple(path_split[2:-1])
assert state in {"cur", "tmp", "new"}
return (mailbox, folder, state)
@staticmethod
def is_uid(uid: typing.Any) -> bool:
"""
Tells if the provided string is a valid UID.
"""
return (
isinstance(uid, str)
and len(uid) == 12
and bool(re.match("^[a-zA-Z0-9+/]{12}$", uid))
)
@staticmethod
def extract_email(field: str) -> str:
"""
Extract the email adress from a To: or From: field
(usually the whole field or between < >)
"""
# TODO Can be made better (extract name and email)
# Also what happens with multiple dests?
try:
sta = field.index("<")
sto = field.index(">")
return field[sta + 1 : sto]
except ValueError:
return field
def retag_msg(self, msg: notmuch.Message) -> None:
"""
Update automatic tags for message.
"""
_, folder, _ = self.get_location(msg)
# Search-friendly folder name
slug_folder_list = list()
for fold_index, fold in [
(fold_index, folder[fold_index]) for fold_index in range(len(folder))
]:
if fold_index == 0 and len(folder) > 1 and fold == "INBOX":
continue
slug_folder_list.append(fold.upper())
slug_folder = tuple(slug_folder_list)
tags = set(msg.get_tags())
def tag_if(tag: str, condition: bool) -> None:
"""
Ensure the presence/absence of tag depending on the condition.
"""
nonlocal msg
if condition and tag not in tags:
msg.add_tag(tag)
elif not condition and tag in tags:
msg.remove_tag(tag)
expeditor = MelEngine.extract_email(msg.get_header("from"))
tag_if("inbox", slug_folder[0] == "INBOX")
tag_if("spam", slug_folder[0] in ("JUNK", "SPAM"))
tag_if("deleted", slug_folder[0] == "TRASH")
tag_if("draft", slug_folder[0] == "DRAFTS")
tag_if("sent", expeditor in self.aliases)
tag_if("unprocessed", False)
# UID
uid = msg.get_header("X-TUID")
if not MelEngine.is_uid(uid):
# TODO Happens to sent mails but should it?
print(f"{msg.get_filename()} has no UID!")
return
uidtag = "tuid{}".format(uid)
# Remove eventual others UID
for tag in tags:
if tag.startswith("tuid") and tag != uidtag:
msg.remove_tag(tag)
msg.add_tag(uidtag)
def apply_msgs(
self,
query_str: str,
action: typing.Callable,
*args: typing.Any,
show_progress: bool = False,
write: bool = False,
close_db: bool = True,
**kwargs: typing.Any,
) -> int:
"""
Run a function on the messages selected by the given query.
"""
self.open_database(write=write)
self.log.info("Querying %s", query_str)
query = notmuch.Query(self.database, query_str)
query.set_sort(notmuch.Query.SORT.OLDEST_FIRST)
elements = query.search_messages()
nb_msgs = query.count_messages()
iterator = (
progressbar.progressbar(elements, max_value=nb_msgs)
if show_progress and nb_msgs
else elements
)
self.log.info("Executing %s", action)
for msg in iterator:
self.log.debug("On mail %s", msg)
if write:
msg.freeze()
action(msg, *args, **kwargs)
if write:
msg.thaw()
msg.tags_to_maildir_flags()
if close_db:
self.close_database()
return nb_msgs
class MelOutput:
"""
All functions that print mail stuff onto the screen.
"""
WIDTH_FIXED = 31
WIDTH_RATIO_DEST_SUBJECT = 0.3
def compute_line_format(
self,
) -> typing.Tuple[typing.Optional[int], typing.Optional[int]]:
"""
Based on the terminal width, assign the width of flexible columns.
"""
if self.is_tty:
columns, _ = shutil.get_terminal_size((80, 20))
remain = columns - MelOutput.WIDTH_FIXED - 1
dest_width = int(remain * MelOutput.WIDTH_RATIO_DEST_SUBJECT)
subject_width = remain - dest_width
return (dest_width, subject_width)
return (None, None)
def __init__(self, engine: MelEngine) -> None:
colorama.init()
self.log = logging.getLogger("MelOutput")
self.engine = engine
self.light_background = True
self.is_tty = sys.stdout.isatty()
self.dest_width, self.subject_width = self.compute_line_format()
self.mailbox_colors: typing.Dict[str, str] = dict()
# TODO Allow custom path
self.caps = mailcap.getcaps()
@staticmethod
def format_date(date: datetime.datetime) -> str:
"""
Format the given date as a 9-characters width string.
Show the time if the mail is less than 24h old,
else show the date.
"""
now = datetime.datetime.now()
if now - date < datetime.timedelta(days=1):
return date.strftime("%H:%M:%S")
if now - date < datetime.timedelta(days=28):
return date.strftime("%d %H:%M")
if now - date < datetime.timedelta(days=365):
return date.strftime("%m-%d %H")
return date.strftime("%y-%m-%d")
@staticmethod
def clip_text(size: typing.Optional[int], text: str) -> str:
"""
Fit text into the given character size,
fill with spaces if shorter,
clip with … if larger.
"""
if size is None:
return text
length = len(text)
if length == size:
return text
if length > size:
return text[: size - 1] + "…"
return text + " " * (size - length)
@staticmethod
def chunks(iterable: str, chunk_size: int) -> typing.Iterable[str]:
"""Yield successive chunk_size-sized chunks from iterable."""
# From https://stackoverflow.com/a/312464
for i in range(0, len(iterable), chunk_size):
yield iterable[i : i + chunk_size]
@staticmethod
def sizeof_fmt(num: int, suffix: str = "B") -> str:
"""
Print the given size in a human-readable format.
"""
remainder = float(num)
# From https://stackoverflow.com/a/1094933
for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]:
if abs(remainder) < 1024.0:
return "%3.1f %s%s" % (remainder, unit, suffix)
remainder /= 1024.0
return "%.1f %s%s" % (remainder, "Yi", suffix)
def get_mailbox_color(self, mailbox: str) -> str:
"""
Return the color of the given mailbox in a ready to print
string with ASCII escape codes.
"""
if not self.is_tty:
return ""
if mailbox not in self.mailbox_colors:
# RGB colors (not supported everywhere)
# color_str = self.config[mailbox]["color"]
# color_str = color_str[1:] if color_str[0] == '#' else color_str
# R = int(color_str[0:2], 16)
# G = int(color_str[2:4], 16)
# B = int(color_str[4:6], 16)
# self.mailbox_colors[mailbox] = f"\x1b[38;2;{R};{G};{B}m"
color_int = int(self.engine.config[mailbox]["color16"])
self.mailbox_colors[mailbox] = f"\x1b[38;5;{color_int}m"
return self.mailbox_colors[mailbox]
def print_msg(self, msg: notmuch.Message) -> None:
"""
Print the given message header on one line.
"""
if not self.dest_width:
self.compute_line_format()
sep = " " if self.is_tty else "\t"
line = ""
tags = set(msg.get_tags())
mailbox, _, _ = self.engine.get_location(msg)
if "unread" in tags or "flagged" in tags:
line += colorama.Style.BRIGHT
# if 'flagged' in tags:
# line += colorama.Style.BRIGHT
# if 'unread' not in tags:
# line += colorama.Style.DIM
line += (
colorama.Back.LIGHTBLACK_EX
if self.light_background
else colorama.Back.BLACK
)
self.light_background = not self.light_background
line += self.get_mailbox_color(mailbox)
# UID
uid = None
for tag in tags:
if tag.startswith("tuid"):
uid = tag[4:]
assert uid, f"No UID for message: {msg}."
assert MelEngine.is_uid(uid), f"{uid} {type(uid)} is not a valid UID."
line += uid
# Date
line += sep + colorama.Fore.MAGENTA
date = datetime.datetime.fromtimestamp(msg.get_date())
line += self.format_date(date)
# Icons
line += sep + colorama.Fore.RED
def tags2col1(
tag1: str, tag2: str, characters: typing.Tuple[str, str, str, str]
) -> None:
"""
Show the presence/absence of two tags with one character.
"""
nonlocal line
both, first, second, none = characters
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", ("?", "S", "D", " "))
tags2col1("attachment", "encrypted", ("E", "A", "E", " "))
tags2col1("unread", "flagged", ("!", "U", "F", " "))
tags2col1("sent", "replied", ("?", "↑", "↪", " "))
# Opposed
line += sep + colorama.Fore.BLUE
if "sent" in tags:
dest = msg.get_header("to")
else:
dest = msg.get_header("from")
line += MelOutput.clip_text(self.dest_width, dest)
# Subject
line += sep + colorama.Fore.WHITE
subject = msg.get_header("subject")
line += MelOutput.clip_text(self.subject_width, subject)
if self.is_tty:
line += colorama.Style.RESET_ALL
print(line)
def notify_msg(self, msg: notmuch.Message) -> None:
"""
Send a notification for the given message.
"""
self.log.info("Sending notification for %s", msg)
subject = msg.get_header("subject")
expd = msg.get_header("from")
account, _, _ = self.engine.get_location(msg)
summary = "{} (<i>{}</i>)".format(html.escape(expd), account)
body = html.escape(subject)
cmd = ["notify-send", "-u", "low", "-i", "mail-message-new", summary, body]
print(" ".join(cmd))
subprocess.run(cmd, check=False)
def notify_all(self) -> None:
"""
Send a notification for unprocessed and unread message.
Basically should only send a notification for a given message once
since it should be marked as processed right after.
"""
nb_msgs = self.engine.apply_msgs(
"tag:unread and tag:unprocessed", self.notify_msg
)
if nb_msgs > 0:
self.log.info("Playing notification sound (%d new message(s))", nb_msgs)
cmd = [
"play",
"-n",
"synth",
"sine",
"E4",
"sine",
"A5",
"remix",
"1-2",
"fade",
"0.5",
"1.2",
"0.5",
"2",
]
subprocess.run(cmd, check=False)
@staticmethod
def format_header_value(val: str) -> str:
"""
Return split header values in a contiguous string.
"""
return val.replace("\n", "").replace("\t", "").strip()
PART_MULTI_FORMAT = (
colorama.Fore.BLUE + "{count} {indent}+ {typ}" + colorama.Style.RESET_ALL
)
PART_LEAF_FORMAT = (
colorama.Fore.BLUE
+ "{count} {indent}→ {desc} ({typ}; {size})"
+ colorama.Style.RESET_ALL
)
def show_parts_tree(
self, part: email.message.Message, depth: int = 0, count: int = 1
) -> int:
"""
Show a tree of the parts contained in a message.
Return the number of parts of the mesage.
"""
indent = depth * "\t"
typ = part.get_content_type()
if part.is_multipart():
print(
MelOutput.PART_MULTI_FORMAT.format(count=count, indent=indent, typ=typ)
)
payl = part.get_payload()
assert isinstance(payl, list)
size = 1
for obj in payl:
size += self.show_parts_tree(obj, depth=depth + 1, count=count + size)
return size
payl = part.get_payload(decode=True)
assert isinstance(payl, bytes)
size = len(payl)
desc = part.get("Content-Description", "<no description>")
print(
MelOutput.PART_LEAF_FORMAT.format(
count=count,
indent=indent,
typ=typ,
desc=desc,
size=MelOutput.sizeof_fmt(size),
)
)
return 1
INTERESTING_HEADERS = ["Date", "From", "Subject", "To", "Cc", "Message-Id"]
HEADER_FORMAT = (
colorama.Fore.BLUE
+ colorama.Style.BRIGHT
+ "{}:"
+ colorama.Style.NORMAL
+ " {}"
+ colorama.Style.RESET_ALL
)
def read_msg(self, msg: notmuch.Message) -> None:
"""
Display the content of a mail.
"""
# Parse
filename = msg.get_filename()
parser = email.parser.BytesParser()
with open(filename, "rb") as filedesc:
mail = parser.parse(filedesc)
# Defects
if mail.defects:
self.log.warning("Defects found in the mail:")
for defect in mail.defects:
self.log.warning(defect)
# Headers
for key in MelOutput.INTERESTING_HEADERS:
val = mail.get(key)
if val:
assert isinstance(val, str)
val = self.format_header_value(val)
print(MelOutput.HEADER_FORMAT.format(key, val))
# TODO Show all headers
# TODO BONUS Highlight failed verifications
self.show_parts_tree(mail)
print()
# Show text/plain
# TODO Consider alternative
for part in mail.walk():
if part.is_multipart():
continue
payl = part.get_payload(decode=True)
assert isinstance(payl, bytes)
if part.get_content_type() == "text/plain":
print(payl.decode())
else:
# TODO Use nametemplate from mailcap
temp_file = "/tmp/melcap.html" # TODO Real temporary file
# TODO FIFO if possible
with open(temp_file, "wb") as temp_filedesc:
temp_filedesc.write(payl)
command, _ = mailcap.findmatch(
self.caps, part.get_content_type(), key="view", filename=temp_file
)
if command:
os.system(command)
def print_dir_list(self) -> None:
"""
Print a colored directory list.
Every line is easilly copiable.
"""
for arb in self.engine.list_folders():
line = colorama.Fore.LIGHTBLACK_EX + "'"
line += self.get_mailbox_color(arb[0])
line += arb[0].replace("'", "\\'")
line += colorama.Fore.LIGHTBLACK_EX
for inter in arb[1:-1]:
line += "/" + inter.replace("'", "\\'")
line += "/" + colorama.Fore.WHITE + arb[-1].replace("'", "\\'")
line += colorama.Fore.LIGHTBLACK_EX + "'"
line += colorama.Style.RESET_ALL
print(line)
class MelCLI:
"""
Handles the user input and run asked operations.
"""
VERBOSITY_LEVELS = ["NOTSET", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
def apply_msgs_input(
self,
argmessages: typing.List[str],
action: typing.Callable,
write: bool = False,
) -> None:
"""
Run a function on the message given by the user.
"""
# TODO First argument might be unecessary
if not argmessages:
from_stdin = not sys.stdin.isatty()
if argmessages:
from_stdin = len(argmessages) == 1 and argmessages == "-"
messages = list()
if from_stdin:
for line in sys.stdin:
uid = line[:12]
if not MelEngine.is_uid(uid):
self.log.error("Not an UID: %s", uid)
continue
messages.append(uid)
else:
for uids in argmessages:
if len(uids) > 12:
self.log.warning(
"Might have forgotten some spaces "
"between the UIDs. Don't worry, I'll "
"split them for you"
)
for uid in MelOutput.chunks(uids, 12):
if not MelEngine.is_uid(uid):
self.log.error("Not an UID: %s", uid)
continue
messages.append(uid)
for message in messages:
query_str = f"tag:tuid{message}"
nb_msgs = self.engine.apply_msgs(
query_str, action, write=write, close_db=False
)
if nb_msgs < 1:
self.log.error("Couldn't execute function for message %s", message)
self.engine.close_database()
def operation_default(self) -> None:
"""
Default operation: list all message in the inbox
"""
self.engine.apply_msgs("tag:inbox", self.output.print_msg)
def operation_inbox(self) -> None:
"""
Inbox operation: list all message in the inbox,
possibly only the unread ones.
"""
query_str = "tag:unread" if self.args.only_unread else "tag:inbox"
self.engine.apply_msgs(query_str, self.output.print_msg)
def operation_flag(self) -> None:
"""
Flag operation: Flag user selected messages.
"""
def flag_msg(msg: notmuch.Message) -> None:
"""
Flag given message.
"""
msg.add_tag("flagged")
self.apply_msgs_input(self.args.message, flag_msg, write=True)
def operation_unflag(self) -> None:
"""
Unflag operation: Flag user selected messages.
"""
def unflag_msg(msg: notmuch.Message) -> None:
"""
Unflag given message.
"""
msg.remove_tag("flagged")
self.apply_msgs_input(self.args.message, unflag_msg, write=True)
def operation_read(self) -> None:
"""
Read operation: show full content of selected message
"""
self.apply_msgs_input(self.args.message, self.output.read_msg)
def operation_fetch(self) -> None:
"""
Fetch operation: Sync remote databases with the local one.
"""
# Fetch mails
self.log.info("Fetching mails")
mbsync_config_file = os.path.expanduser("~/.config/mbsyncrc") # TODO Better
cmd = ["mbsync", "--config", mbsync_config_file, "--all"]
subprocess.run(cmd, check=False)
# Index new mails
self.engine.notmuch_new()
# Notify
self.output.notify_all()
# Tag new mails
self.engine.apply_msgs(
"tag:unprocessed", self.engine.retag_msg, show_progress=True, write=True
)
def operation_list(self) -> None:
"""
List operation: Print all folders.
"""
self.output.print_dir_list()
def operation_debug(self) -> None:
"""
DEBUG
"""
print("UwU")
def operation_retag(self) -> None:
"""
Retag operation: Manually retag all the mails in the database.
Mostly debug I suppose.
"""
self.engine.apply_msgs(
"*", self.engine.retag_msg, show_progress=True, write=True
)
def operation_all(self) -> None:
"""
All operation: list every single message.
"""
self.engine.apply_msgs("*", self.output.print_msg)
def add_subparsers(self) -> None:
"""
Add the operation parser to the main parser.
"""
# TODO If the only operation to the parser done are adding argument,
# we should automate this.
subparsers = self.parser.add_subparsers(help="Action to execute")
# List messages
self.parser.set_defaults(operation=self.operation_default)
# inbox (default)
parser_inbox = subparsers.add_parser(
"inbox", help="Show unread, unsorted and flagged messages"
)
parser_inbox.add_argument(
"-u", "--only-unread", action="store_true", help="Show unread messages only"
)
# TODO Make this more relevant
parser_inbox.set_defaults(operation=self.operation_inbox)
# list folder [--recurse]
# List actions
parser_list = subparsers.add_parser("list", help="List all folders")
# parser_list.add_argument('message', nargs='*', help="Messages")
parser_list.set_defaults(operation=self.operation_list)
# flag msg...
parser_flag = subparsers.add_parser("flag", help="Mark messages as flagged")
parser_flag.add_argument("message", nargs="*", help="Messages")
parser_flag.set_defaults(operation=self.operation_flag)
# unflag msg...
parser_unflag = subparsers.add_parser(
"unflag", help="Mark messages as not-flagged"
)
parser_unflag.add_argument("message", nargs="*", help="Messages")
parser_unflag.set_defaults(operation=self.operation_unflag)
# delete msg...
# spam msg...
# move dest msg...
# Read message
# read msg [--html] [--plain] [--browser]
parser_read = subparsers.add_parser("read", help="Read message")
parser_read.add_argument("message", nargs=1, help="Messages")
parser_read.set_defaults(operation=self.operation_read)
# attach msg [id] [--save] (list if no id, xdg-open else)
# Redaction
# new account
# reply msg [--all]
# Folder management
# tree [folder]
# mkdir folder
# rmdir folder (prevent if folder isn't empty (mail/subfolder))
# (yeah that should do)
# Meta
# setup (interactive thing maybe)
# fetch (mbsync, notmuch new, retag, notify; called by greater gods)
parser_fetch = subparsers.add_parser(
"fetch", help="Fetch mail, tag them, and run notifications"
)
parser_fetch.set_defaults(operation=self.operation_fetch)
# Debug
# debug (various)
parser_debug = subparsers.add_parser(
"debug", help="Who know what this holds..."
)
parser_debug.set_defaults(verbosity="DEBUG")
parser_debug.set_defaults(operation=self.operation_debug)
# retag (all or unprocessed)
parser_retag = subparsers.add_parser(
"retag", help="Retag all mails (when you changed configuration)"
)
parser_retag.set_defaults(operation=self.operation_retag)
# all
parser_all = subparsers.add_parser("all", help="Show ALL messages")
parser_all.set_defaults(operation=self.operation_all)
def create_parser(self) -> argparse.ArgumentParser:
"""
Create the main parser that will handle the user arguments.
"""
parser = argparse.ArgumentParser(description="Meh mail client")
parser.add_argument(
"-v",
"--verbosity",
choices=MelCLI.VERBOSITY_LEVELS,
default="WARNING",
help="Verbosity of self.log messages",
)
# parser.add_argument('-n', '--dry-run', action='store_true',
# help="Don't do anything") # DEBUG
default_config_file = os.path.join(
xdg.BaseDirectory.xdg_config_home, "mel", "accounts.conf"
)
parser.add_argument(
"-c", "--config", default=default_config_file, help="Accounts config file"
)
return parser
def __init__(self) -> None:
self.log = logging.getLogger("MelCLI")
self.parser = self.create_parser()
self.add_subparsers()
self.args = self.parser.parse_args()
coloredlogs.install(
level=self.args.verbosity, fmt="%(levelname)s %(name)s %(message)s"
)
self.engine = MelEngine(self.args.config)
self.output = MelOutput(self.engine)
if self.args.operation:
self.log.info("Executing operation %s", self.args.operation)
self.args.operation()
if __name__ == "__main__":
if not os.environ.get("MEL_DEBUG"):
CLI = MelCLI()
else:
try:
CLI = MelCLI()
except:
EXTYPE, VALUE, TB = sys.exc_info()
traceback.print_exc()
pdb.post_mortem(TB)

View file

@ -0,0 +1,344 @@
#!/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 Signature file
# TODO Write ~/.mail/[mailbox]/color file if required by sth?
# TODO Write in .config or .cache /mel
# TODO Fix IMAPS with mbsync
configPath = os.path.join(os.path.expanduser("~"), ".config", "mel", "accounts.conf")
config = configparser.ConfigParser()
config.read(configPath)
storageFull = os.path.realpath(os.path.expanduser(config["GENERAL"]["storage"]))
config["GENERAL"]["storage"] = storageFull
SERVER_DEFAULTS = {
"imap": {"port": 143, "starttls": True},
"smtp": {"port": 587, "starttls": True},
}
SERVER_ITEMS = {"host", "port", "user", "pass", "starttls"}
ACCOUNT_DEFAULTS = {
"color": "#FFFFFF",
"color16": "0",
# "colormutt": "white",
"inboxfolder": "INBOX",
"archivefolder": "Archive",
"draftsfolder": "Drafts",
"sentfolder": "Sent",
"spamfolder": "Spam",
"trashfolder": "Trash",
}
# Reading sections
accounts = dict()
mails = set()
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]
for k, v in config["DEFAULT"].items():
if k not in data:
data[k] = v
for k, v in ACCOUNT_DEFAULTS.items():
if k not in data:
data[k] = v
mails.add(section["from"])
if "alternatives" in section:
for alt in section["alternatives"].split(";"):
mails.add(alt)
data["account"] = name
data["storage"] = os.path.join(config["GENERAL"]["storage"], name)
data["storageInbox"] = os.path.join(data["storage"], "INBOX")
accounts[name] = data
general = dict()
section = config["GENERAL"]
for key in section.keys():
general[key] = section[key]
general["main"] = accounts[general["main"]]
# 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 {account}]
localrepository = {account}-local
remoterepository = {account}-remote
autorefresh = 0.5
quick = 10
utf8foldernames = yes
postsynchook = ~/.mutt/postsync
[Repository {account}-local]
type = Maildir
localfolders = {storage}
[Repository {account}-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 {account}
Host {imaphost}
Port {imapport}
User {imapuser}
Pass "{imappassEscaped}"
{secconf}
IMAPStore {account}-remote
Account {account}
MaildirStore {account}-local
Subfolders Verbatim
Path {storage}/
Inbox {storageInbox}/
Channel {account}
Master :{account}-remote:
Slave :{account}-local:
Patterns *
Create Both
SyncState *
"""
mbsyncStr = ""
for name, account in accounts.items():
if account["imapstarttls"]:
secconf = "SSLType STARTTLS"
else:
secconf = "SSLType IMAPS"
if "certificate" in account:
secconf += "\nCertificateFile {certificate}".format(**account)
imappassEscaped = account["imappass"].replace("\\", "\\\\")
mbsyncStr += MBSYNC_ACCOUNT.format(
**account, secconf=secconf, imappassEscaped=imappassEscaped
)
mbsyncFilepath = os.path.join(os.path.expanduser("~"), ".config/mel/mbsyncrc")
with open(mbsyncFilepath, "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 {account}
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)
mbsyncFilepath = os.path.join(os.path.expanduser("~"), ".config/msmtp/config")
with open(mbsyncFilepath, "w") as f:
f.write(msmtpStr)
# notmuch
NOTMUCH_BEGIN = """[database]
path={storage}
[user]
name={main[name]}
primary_email={main[from]}
other_email={other_email}
[new]
tags=unprocessed;unread;
ignore=
[search]
exclude_tags=deleted;spam;
[maildir]
synchronize_flags=true
[crypto]
gpg_path=gpg
"""
other_email = mails.copy()
other_email.remove(general["main"]["from"])
other_email = ";".join(other_email)
notmuchStr = NOTMUCH_BEGIN.format(**general, other_email=other_email)
mbsyncFilepath = os.path.join(os.path.expanduser("~"), ".config/notmuch-config")
with open(mbsyncFilepath, "w") as f:
f.write(notmuchStr)
# mutt (temp)
## mailboxes
MAILBOXES_BEGIN = "mailboxes"
mailboxesStr = MAILBOXES_BEGIN
for name, account in accounts.items():
lines = "-" * (20 - len(name))
mailboxesStr += f' "+{name}{lines}"'
for root, dirs, files in os.walk(account["storage"]):
if "cur" not in dirs or "new" not in dirs or "tmp" not in dirs:
continue
assert root.startswith(storageFull)
path = root[len(storageFull) + 1 :]
mailboxesStr += f' "+{path}"'
mailboxesStr += "\n"
mailboxesFilepath = os.path.join(os.path.expanduser("~"), ".mutt/mailboxes")
with open(mailboxesFilepath, "w") as f:
f.write(mailboxesStr)
## accounts
# TODO html mails
MUTT_ACCOUNT = """set from = "{from}"
set sendmail = "/usr/bin/msmtp -a {account}"
set realname = "{name}"
set spoolfile = "+{account}/{inboxfolder}"
set mbox = "+{account}/{archivefolder}"
set postponed = "+{account}/{draftsfolder}"
set record = "+{account}/{sentfolder}"
set trash = "+{account}/{trashfolder}"
set signature = "~/.mutt/accounts/{account}.sig"
set content_type = "text/plain"
set sig_dashes = yes
color status {colormutt} default
macro index D \\
"<clear-flag>N<save-message>+{account}/{trashfolder}<enter>" \\
"move message to the trash"
macro index S \\
"<clear-flag>N<save-message>+{account}/{spamfolder}<enter>" \\
"mark message as spam"
# vim: syntax=muttrc
"""
for name, account in accounts.items():
muttStr = MUTT_ACCOUNT.format(**account)
# Config
muttFilepath = os.path.join(os.path.expanduser("~"), f".mutt/accounts/{name}")
with open(muttFilepath, "w") as f:
f.write(muttStr)
# Signature
sigStr = account.get("sig", account.get("name", ""))
sigFilepath = os.path.join(os.path.expanduser("~"), f".mutt/accounts/{name}.sig")
with open(sigFilepath, "w") as f:
f.write(sigStr)
MUTT_SELECTOR = """
set folder = "{storage}"
source ~/.mutt/mailboxes
source ~/.mutt/accounts/{main[account]}
{hooks}
source ~/.mutt/custom
# vim: syntax=muttrc
"""
selectStr = ""
hooks = ""
for name, account in accounts.items():
hooks += f"folder-hook {name}/* source ~/.mutt/accounts/{name}\n"
selectStr += MUTT_SELECTOR.format(**general, hooks=hooks)
selectFilepath = os.path.join(os.path.expanduser("~"), ".mutt/muttrc")
with open(selectFilepath, "w") as f:
f.write(selectStr)
## Color
for name, account in accounts.items():
# Config
colorFilepath = os.path.join(
os.path.expanduser("~"), f'{general["storage"]}/{name}/color'
)
with open(colorFilepath, "w") as f:
f.write(account["color"])

View file

@ -0,0 +1,39 @@
#!/usr/bin/env python3
import logging
import os
import shutil
import sys
import coloredlogs
coloredlogs.install(level="DEBUG", fmt="%(levelname)s %(message)s")
log = logging.getLogger()
MUSICS_FOLDER = os.path.join(os.path.expanduser("~"), "Musique")
BOF_FOLDER = os.path.join(os.path.expanduser("~"), ".MusiqueBof")
for f in sys.argv[1:]:
src = os.path.realpath(f)
if not os.path.isfile(src):
log.error("{} does not exists".format(src))
continue
srcBase = None
if src.startswith(MUSICS_FOLDER):
srcBase = MUSICS_FOLDER
dstBase = BOF_FOLDER
elif src.startswith(BOF_FOLDER):
srcBase = BOF_FOLDER
dstBase = MUSIC_FOLDER
else:
log.error("{} not in any music folder".format(src))
continue
common = os.path.relpath(src, srcBase)
dst = os.path.join(dstBase, common)
dstFolder = os.path.dirname(dst)
log.info("{} → {}".format(src, dst))
os.makedirs(dstFolder, exist_ok=True)
shutil.move(src, dst)

83
unprocessed/config/scripts/nv Executable file
View file

@ -0,0 +1,83 @@
#!/usr/bin/env bash
# Extracted frm nvidia-xrun
DRY_RUN=0
function execute {
if [[ ${DRY_RUN} -eq 1 ]]
then
echo ">>Dry run. Command: $*"
else
eval $*
fi
}
function turn_off_gpu {
if [[ "$REMOVE_DEVICE" == '1' ]]; then
echo 'Removing Nvidia bus from the kernel'
execute "sudo tee /sys/bus/pci/devices/${DEVICE_BUS_ID}/remove <<<1"
else
echo 'Enabling powersave for the graphic card'
execute "sudo tee /sys/bus/pci/devices/${DEVICE_BUS_ID}/power/control <<<auto"
fi
echo 'Enabling powersave for the PCIe controller'
execute "sudo tee /sys/bus/pci/devices/${CONTROLLER_BUS_ID}/power/control <<<auto"
}
function turn_on_gpu {
echo 'Turning the PCIe controller on to allow card rescan'
execute "sudo tee /sys/bus/pci/devices/${CONTROLLER_BUS_ID}/power/control <<<on"
echo 'Waiting 1 second'
execute "sleep 1"
if [[ ! -d /sys/bus/pci/devices/${DEVICE_BUS_ID} ]]; then
echo 'Rescanning PCI devices'
execute "sudo tee /sys/bus/pci/rescan <<<1"
echo "Waiting ${BUS_RESCAN_WAIT_SEC} second for rescan"
execute "sleep ${BUS_RESCAN_WAIT_SEC}"
fi
echo 'Turning the card on'
execute "sudo tee /sys/bus/pci/devices/${DEVICE_BUS_ID}/power/control <<<on"
}
function load_modules {
for module in "${MODULES_LOAD[@]}"
do
echo "Loading module ${module}"
execute "sudo modprobe ${module}"
done
}
function unload_modules {
for module in "${MODULES_UNLOAD[@]}"
do
echo "Unloading module ${module}"
execute "sudo modprobe -r ${module}"
done
}
if [[ "$1" == "-d" ]]
then
DRY_RUN=1
shift 1
fi
# load config file
. /etc/default/nvidia-xrun
if [ "$1" == "on" ]
then
turn_on_gpu
load_modules
elif [ "$1" == "off" ]
then
unload_modules
turn_off_gpu
else
echo "Usage: $0 [on|off]"
fi

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,22 @@
{
"name": "geoffreyfrogeye-dotfiles-scripts",
"version": "1.0.0",
"description": "Stores dependencies used for GeoffreyFrogeye's dotfiles scripts.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "https://git.frogeye.fr/geoffrey/dotfiles"
},
"author": "GeoffreyFrogeye",
"license": "GPL-3.0",
"dependencies": {
"highlight.js": "^9.11.0",
"html-pdf": "^2.1.0",
"katex": "^0.7.1",
"marked": "^0.3.6",
"yargs": "^8.0.1"
}
}

103
unprocessed/config/scripts/proxy Executable file
View file

@ -0,0 +1,103 @@
#!/usr/bin/env bash
# Allows easy manipulation of the proxy variables
function proxy_set_help {
echo "Usage: $0 set ADDRESS"
echo
echo "Arguments:"
echo " ADDRESS Address of the proxy"
echo
echo "Examples:"
echo ' eval "$(proxy set http://proxy.mycompany.com:3128/)"'
return 0
}
function proxy_set {
if [ -z $1 ]; then
proxy_set_help
return 1
fi
echo "export http_proxy='$1'"
echo "export https_proxy='$1'"
echo "export ftp_proxy='$1'"
echo "export rsync_proxy='$1'"
exit 0
}
function proxy_setup_help {
echo "Usage: $0 setup"
echo
echo "Examples:"
echo " proxy_set # Then eval the output"
return 0
}
function proxy_setup {
export no_proxy="localhost,127.0.0.1,localaddress,.localdomain.com"
if (( $# > 0 )); then
valid=$(echo $@ | sed -n 's/\([0-9]\{1,3\}.\)\{4\}:\([0-9]\+\)/&/p')
if [[ $valid != $@ ]]; then
>&2 echo "Invalid address"
return 1
fi
proxy_set "http://$1/"
return 0
fi
echo -n "User: "; read username
if [[ $username != "" ]]; then
echo -n "Password: "
read -es password
local pre="$username:$password@"
fi
echo -n "Server: "; read server
echo -n "Port: "; read port
proxy_set "http://$pre$server:$port/"
return 0
}
function proxy_off_help {
echo "Usage: $0 off"
echo
echo "Examples:"
echo ' eval $(proxy off)'
return 0
}
function proxy_off {
echo 'unset http_proxy'
echo 'unset https_proxy'
echo 'unset ftp_proxy'
echo 'unset rsync_proxy'
return 0
}
function proxy_help {
command="$1"
if [ -n "$command" ]; then
if type "proxy_${command}_help" &> /dev/null; then
shift
"proxy_${command}_help" "$@"
return $?
fi
fi
echo "Usage: $0 COMMAND"
echo
echo "Commands:"
echo " setup Interactively setup proxy"
echo " set Set proxy from address"
echo " off Turn off proxy"
echo " help Get help with commands"
return 0
}
# MAIN
command="$1"
shift
if type "proxy_$command" &> /dev/null; then
"proxy_$command" "$@"
else
proxy_help
fi

View file

@ -0,0 +1,14 @@
#!/usr/bin/env bash
#! nix-shell -i bash --pure
#! nix-shell -p bash
# Removes CRLF (^M or \r) from a file
#sed -e "s/^M//" "$1" -i
tmpfile=$(mktemp)
cp "$1" "$tmpfile"
tr -d '\r' < "$tmpfile" > "$1"
rm "$tmpfile"

View file

@ -0,0 +1,24 @@
#!/usr/bin/env bash
# Dynamically determines if the ssh connection
# is to be proxied through `proxytunnel`
# To be used with ssh_config ProxyCommand
host="$1"
port="$2"
if [ -z "$http_proxy" ]; then
socat "TCP:$host:$port" -
else
proxy=$(echo "$http_proxy" | sed 's/^https\?:\/\///' | sed 's/\/$//')
port=443 # Most won't want this
echo "$proxy" | grep '@'
if [ $? == 0 ]; then
user=$(echo $proxy | cut -d '@' -f 2)
proxy=$(echo $proxy | cut -d '@' -f 1)
proxytunnel -p $proxy -P $user -d $host:$port
else
proxytunnel -p $proxy -d $host:$port
fi
fi

193
unprocessed/config/scripts/tvshow Executable file
View file

@ -0,0 +1,193 @@
#!/usr/bin/env python3
# pylint: disable=C0103,W0621
# pip install tmdbv3api
import os
import re
import subprocess
import sys
import typing
import tmdbv3api
# TODO Override files without warning
# TODO Dry run mode (just comment the last line ^^)
# Typing
Episode = typing.Any # TODO
# Constants
API_KEY_PASS_PATH = "http/themoviedb.org"
VIDEO_EXTENSIONS = {"mp4", "mkv", "avi", "webm"}
# Functions
def get_pass_data(path: str) -> typing.Dict[str, str]:
"""
Returns the data stored in the Unix password manager
given its path.
"""
run = subprocess.run(["pass", path], stdout=subprocess.PIPE, check=True)
lines = run.stdout.decode().split("\n")
data = dict()
data["pass"] = lines[0]
for line in lines[1:]:
match = re.match(r"(\w+): ?(.+)", line)
if match:
data[match[1]] = match[2]
return data
def confirm(text: str) -> bool:
res = input(text + " [yn] ")
while res not in ("y", "n"):
res = input("Please answer with y or n: ")
return res == "y"
def episode_identifier(episode: typing.Any) -> str:
return (
f"S{episode['season_number']:02d}E"
+ f"{episode['episode_number']:02d} {episode['name']}"
)
dryrun = "-n" in sys.argv
if dryrun:
dryrun = True
sys.argv.remove("-n")
# Connecting to TMBDB
tmdb = tmdbv3api.TMDb()
tmdb.api_key = get_pass_data(API_KEY_PASS_PATH)["api"]
tmdb.language = sys.argv[1]
# Searching the TV show name (by current directory name)
tv = tmdbv3api.TV()
season = tmdbv3api.Season()
if len(sys.argv) >= 3:
show_name = sys.argv[2]
else:
show_name = os.path.split(os.path.realpath(os.path.curdir))[1]
if "(" in show_name:
show_name = show_name.split("(")[0].strip()
search = tv.search(show_name)
# Asking the user to select the one
show = None
for res in search:
print(f"#{res.id} {res.name} ({res.first_air_date[:4]}): {res.overview}")
if confirm("Is this the show for this folder?"):
show = tv.details(res.id)
break
if not show:
print("Could not find a matching " + f"show on TheMovieDatabase for {show_name}.")
sys.exit(1)
# Retrieving all the episode of the show
episodes: typing.List[Episode] = list()
print(f"List of episodes for {show.name}:")
for season_number in range(0, show.number_of_seasons + 1):
season_details = season.details(show.id, season_number)
try:
season_details.episodes
except AttributeError:
continue
for episode in season_details.episodes:
episodes.append(episode)
print(f"- {episode_identifier(episode)}")
# Finding movie files in the folder
print("List of video files in this folder")
videos: typing.List[typing.Tuple[str, str]] = list()
for root, dirs, files in os.walk(os.path.curdir):
for filename in files:
basename, ext = os.path.splitext(filename)
real_ext = ext[1:].lower()
if real_ext not in VIDEO_EXTENSIONS:
continue
videos.append((root, filename))
print(f"- {filename}")
def get_episode(season_number: int, episode_number: int) -> typing.Optional[Episode]:
# TODO Make more efficient using indexing
for episode in episodes:
if (
episode["season_number"] == season_number
and episode["episode_number"] == episode_number
):
return episode
return None
# Matching movie files to episode
associations: typing.List[typing.Tuple[typing.Tuple[str, str], Episode]] = list()
for video in videos:
root, filename = video
match = re.search(r"S(\d+)E(\d+)", filename)
print(f"Treating file: {root}/{filename}")
episode = None
season_number = 0
episode_number = 0
while not episode:
if match:
season_number = int(match[1])
episode_number = int(match[2])
else:
try:
season_number = int(input("Season number ?"))
episode_number = int(input("Episode number ?"))
except ValueError:
continue
if season_number < 0 and episode_number < 0:
break
match = None
episode = get_episode(season_number, episode_number)
if not episode:
print(
f" could not find episode S{season_number:02d}E{episode_number:02d} in TMBD"
)
# Skip
if not episode:
if season_number < -1 and episode_number < -1:
# Skip all
break
# Skip one
continue
associations.append((video, episode))
print(f" associated to: {episode_identifier(episode)}")
# Rename video files
for association in associations:
video, episode = association
root, filename = video
basename, ext = os.path.splitext(filename)
new_name = f"{show.name} ({show.first_air_date[:4]}) {episode_identifier(episode)}"
# Rename all file with the same base name as the original file so we
# can rename nfo files and subtitles (only one though)
for a_filename in os.listdir(root):
a_basename, a_ext = os.path.splitext(a_filename)
if a_basename == basename:
old_path = os.path.join(root, a_filename)
new_path = os.path.join(root, new_name + a_ext)
if old_path == new_path:
continue
print(old_path, "->", new_path)
if not dryrun:
os.rename(old_path, new_path)

View file

@ -0,0 +1,3 @@
#!/usr/bin/env bash
echo ssh "$1" sudo date --set="'$(date -R)'"

View file

@ -0,0 +1 @@
*.wants/*

View file

@ -0,0 +1,10 @@
[Unit]
Description=IPFS daemon
After=network.target
[Service]
ExecStart=/usr/bin/ipfs daemon
Restart=on-failure
[Install]
WantedBy=default.target

View file

@ -0,0 +1,6 @@
[Unit]
Description=Meh mail client new mail fetcher
[Service]
Type=oneshot
ExecStart=/home/geoffrey/.config/scripts/mel fetch

View file

@ -0,0 +1,10 @@
[Unit]
Description=Meh mail client fetcher timer
[Timer]
OnBootSec=2m
OnUnitActiveSec=5m
Unit=melfetch.service
[Install]
WantedBy=timers.target

View file

@ -0,0 +1,10 @@
[Unit]
Description=Urxvt Terminal Daemon
Requires=urxvtd.socket
[Service]
ExecStart=/usr/bin/urxvtd -o -q
Environment=RXVT_SOCKET=%t/urxvtd-%H
[Install]
WantedBy=default.target

View file

@ -0,0 +1,9 @@
[Unit]
Description=urxvt daemon (socket activation)
Documentation=man:urxvtd(1) man:urxvt(1)
[Socket]
ListenStream=%t/urxvtd-%H
[Install]
WantedBy=sockets.target

View file

@ -0,0 +1,13 @@
[Unit]
Description=Remote desktop service (VNC)
[Service]
Type=simple
# wait for Xorg started by ${USER}
ExecStartPre=/bin/sh -c 'while ! pgrep -U "$USER" Xorg; do sleep 2; done'
ExecStart=/usr/bin/x0vncserver -rfbauth /home/${USER}/.vnc/passwd
# or login with your username & password
#ExecStart=/usr/bin/x0vncserver -PAMService=login -PlainUsers=${USER} -SecurityTypes=TLSPlain
[Install]
WantedBy=default.target

View file

@ -0,0 +1,4 @@
[main]
path = ~/.cache/vdirsyncer/calendars/*
default_list = Personnel
humanize = True

View file

@ -0,0 +1 @@
theme.css

View file

@ -0,0 +1,105 @@
" Trydactyl (vim-like keybindings for Firefox) configuration file
" Make sure to have the native messenger installed (:native)
" Fair part took from https://gist.github.com/BurntSushi/393546a65db38d57cedcfd72c6d89bf3
" Reset parameters to tridactyl's default
" so there's nothing being kept when :source ing
sanitise tridactyllocal tridactylsync
colors dark
" For when https://github.com/bezmi/base16-tridactyl/issues/2 will be closed
" colors theme
" I use H and L for moving accross windows,
" so I'd rather use those for tabs too
bind H tabprev
bind L tabnext
bind J back
bind K forward
" I prefer the qutebrowser-style keybindings for opening stuff in a new tab
" Also it is consistent with f/F p/P s/S... in tridactyl itself
bind o fillcmdline open
bind O fillcmdline tabopen
" Let's reuse t/T to use that useful feature of editing the current URL before
" opening
" bind t current_url open
" bint T current_url tabopen
bind t composite url2args | fillcmdline open
bind T composite url2args | fillcmdline tabopen
" We will let the w/W the same, as in my case tab=window
" Add to ...
unbind a
unbind A
" Reorder bookmarks/quickmarks
bind M current_url bmark
" Activate the rudimentary search feature
" Not activated by default because `incsearch` is not implemented,
" so since I don't know what it is I don't care about it
bind / fillcmdline find
bind ? fillcmdline find -?
bind n findnext 1
bind N findnext -1
" Remove search highlighting
bind ,<Space> nohlsearch
" Ctrl-F should use the browser's native 'find' functionality.
unbind <C-f>
" Subscribe to RSS
bind af rssexec
set rsscmd tabopen add_rss %u
set searchurls.add_rss https://rss.frogeye.fr/i/?c=feed&a=add&url_rss=%s
bind as composite get_current_url | tabopen add_links
set searchurls.add_links https://links.frogeye.fr/?post=%s
" New tab page (this link won't work for you :P)
set newtab https://geoffrey.frogeye.fr/home.php
" Search engines
set searchengine qwant
set searchurls.arch https://wiki.archlinux.org/?search=%s
set searchurls.archp https://www.archlinux.org/packages/?q=%s
set searchurls.aur https://aur.archlinux.org/packages/?K=%s
set searchurls.aw http://www.amp-what.com/unicode/search/%s
set searchurls.bulbapedia https://bulbapedia.bulbagarden.net/w/index.php?title=Special:Search&search=%s&go=Go
set searchurls.fdroid https://search.f-droid.org/?q=%s
set searchurls.gfr https://www.google.fr/search?hl=fr&q=%s
set searchurls.g https://www.google.fr/search?q=%s
set searchurls.gihpy https://giphy.com/search/%s
set searchurls.gi http://images.google.com/search?q=%s
set searchurls.github https://github.com/search?q=%s
set searchurls.npm https://www.npmjs.com/search?q=%s
set searchurls.pypi https://pypi.org/search/?q=%s
set searchurls.python https://docs.python.org/3/search.html?q=%s
set searchurls.qwant https://www.qwant.com/?t=web&q=%s
set searchurls.invidious https://invidious.drycat.fr/search?q=%s
set searchurls.id https://invidious.drycat.fr/search?q=%s
set searchurls.wa https://www.wolframalpha.com/input/?i=%s
set searchurls.yt https://www.youtube.com/results?search_query=%s
" Firefox GUI
" This can still be shown with F6!
" guiset_quiet gui none
" Never autofocus
set allowautofocus false
" Hide the mode indicator in the lower right corner
set modeindicator false
" Hint chars
" As I take more time finding the key I need to type (even if it's on the home
" row) than moving my fingers to get it, I prefer to reduce the number of keys
" to type rather than the movement of my fingers, hence the big amount of
" hint chars
set hintchars asdfhjklgqweryuioptzxcbnmv1234678905
" This will have to do until someone writes us a nice syntax file :)
" vim: set filetype=vim:

View file

View file

@ -0,0 +1,70 @@
# An example configuration for vdirsyncer.
#
# Move it to ~/.cache/vdirsyncer/config or ~/.config/vdirsyncer/config and edit it.
# Run `vdirsyncer --help` for CLI usage.
#
# Optional parameters are commented out.
# This file doesn't document all available parameters, see
# http://vdirsyncer.pimutils.org/ for the rest of them.
[general]
# A folder where vdirsyncer can store some metadata about each pair.
status_path = "~/.cache/vdirsyncer/status/"
# # CARDDAV
[pair geoffrey_contacts]
# A `[pair <name>]` block defines two storages `a` and `b` that should be
# synchronized. The definition of these storages follows in `[storage <name>]`
# blocks. This is similar to accounts in OfflineIMAP.
a = "geoffrey_contacts_local"
b = "geoffrey_contacts_remote"
# Synchronize all collections that can be found.
# You need to run `vdirsyncer discover` if new calendars/addressbooks are added
# on the server.
collections = ["from a", "from b"]
# Synchronize the "display name" property into a local file (~/.contacts/displayname).
metadata = ["displayname"]
# To resolve a conflict the following values are possible:
# `null` - abort when collisions occur (default)
# `"a wins"` - assume a's items to be more up-to-date
# `"b wins"` - assume b's items to be more up-to-date
#conflict_resolution = null
[storage geoffrey_contacts_local]
# A storage references actual data on a remote server or on the local disk.
# Similar to repositories in OfflineIMAP.
type = "filesystem"
path = "~/.cache/vdirsyncer/contacts/"
fileext = ".vcf"
[storage geoffrey_contacts_remote]
type = "carddav"
url = "https://cloud.frogeye.fr/remote.php/dav"
username = "geoffrey"
password.fetch = ["command", "sh", "-c", "cat ~/.config/vdirsyncer/pass"]
# CALDAV
[pair geoffrey_calendar]
a = "geoffrey_calendar_local"
b = "geoffrey_calendar_remote"
collections = ["from a", "from b"]
# Calendars also have a color property
metadata = ["displayname", "color"]
# conflict_resolution = "a wins"
# conflict_resolution = "b wins"
[storage geoffrey_calendar_local]
type = "filesystem"
path = "~/.cache/vdirsyncer/calendars/"
fileext = ".ics"
[storage geoffrey_calendar_remote]
type = "caldav"
url = "https://cloud.frogeye.fr/remote.php/dav"
username = "geoffrey"
password.fetch = ["command", "sh", "-c", "cat ~/.config/vdirsyncer/pass"]

103
unprocessed/config/xinitrc Executable file
View file

@ -0,0 +1,103 @@
#!/bin/sh
#
# ~/.xinitrc
#
# Executed by startx
#
# Execute system modules, just in case
if [ -d /etc/X11/xinit/xinitrc.d ]; then
for f in /etc/X11/xinit/xinitrc.d/*; do
[ -x "$f" ] && . "$f"
done
unset f
fi
# Load Xresources
[ -f ~/.config/Xresources/main ] && xrdb -I"$HOME" ~/.config/Xresources/main
# Disable the bell
xset b off
# Folders to search for desktop environments (DE) in
sessions_dirs="/usr/share/xsessions"
# If we have locally installed DE try them before system ones
sessions_dir_local="$HOME/.local/share/xsessions"
if [ -d "$sessions_dir_local" ]
then
sessions_dirs="$sessions_dir_local $sessions_dirs"
fi
# If we have junest installed DE try them before all others
sessions_dir_junest="$HOME/.junest/usr/share/xsessions"
if [ -d "$sessions_dir_junest" ]
then
sessions_dirs="$sessions_dir_junest $sessions_dirs"
fi
startSession() {
session_dir="$1"
session_name="$2"
session_file="${session_dir}/${session_name}.desktop"
executable="$(grep ^Exec= "$session_file" | cut -d'=' -f2)"
# TODO Does this work with parameters?
# gnome-classic might need some
# If this is a Junest DE, we need to wrap it
if [ "$sessions_dir" = "$sessions_dir_junest" ]
then
# Some DMs enforce that to you,
# which shows warning on Junest
unset LD_PRELOAD
# The intended way:
# exec ~/.local/bin/junest "$executable" $parameters
# Too restricted to the outside (e.g. Yubikey isn't accessible)
# The custom way
exec ~/.local/bin/junest --backend-args "--dev-bind /run /run" "$executable" $parameters
# The fallback wrappers way
# export PATH="$PATH:~/.junest/usr/bin_wrappers"
# exec "$executable" $parameters
# Should work but doesn't, I forgot why
# The "I do what I want" way
#exec bwrap --bind $HOME/.junest / --bind $HOME $HOME --bind /tmp /tmp --proc /proc --dev-bind /dev /dev --dev-bind /run /run "$executable" $parameters
# Even Alacritty doesn't work here
fi
exec "$executable" $parameters
}
trySession() { # session_name
session_name="$1"
for sessions_dir in $sessions_dirs
do
session_file="$sessions_dir/${session_name}.desktop"
if [ -f "$session_file" ]
then
startSession "$sessions_dir" "$session_name"
fi
done
}
if [ -n "$1" ]
then
trySession "$1"
else
trySession i3
trySession xfce4
trySession mate
trySession plasma
trySession gnome
trySession kde
fi
# If we found nothing by then, RIP
echo "Couldn't find a suitable DM."
exit 1

1
unprocessed/termux/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
colors.properties

2
unprocessed/termux/bin/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*
!.gitignore

View file

@ -0,0 +1,2 @@
#!/data/data/com.termux/files/usr/bin/bash
autosvc

View file

@ -0,0 +1,4 @@
#!/data/data/com.termux/files/usr/bin/bash
echo "/system/bin/mount -o remount,rw /" | tsu
echo "ln -s /data/data/com.termux/files/usr /usr" | tsu
echo "/system/bin/mount -o remount,ro /" | tsu

BIN
unprocessed/termux/font.ttf Normal file

Binary file not shown.

View file

@ -0,0 +1,39 @@
#!/data/data/com.termux/files/usr/bin/bash
#
# Start services based on phone state
#
function act() {
# Services that should be always on
service sshd start
service autosvc start
# Services that should be on depending on battery
bat="$(termux-battery-status | jq -r '.status')"
if [[ "$bat" == "FULL" || "$bat" == "CHARGING" ]]
then
service syncthing start
else
service syncthing stop
fi
}
if [ "$1" == "-d" ]
then
# Daemon mode
while true
do
echo 29
act &>> $HOME/.local/log/autosvc.log
echo 31
sleep 60
done
else
# One shot mode
# TODO Soft-code the log destination & the program arguments
act
fi

View file

@ -0,0 +1,22 @@
#!/data/data/com.termux/files/usr/bin/bash
#
# Run & stop Termux services
#
if [ $# -lt 1 ]
then
echo "Expected a service name as first argument."
exit 1
fi
service="$1"
file="$HOME/.termux/services/$1"
if [ -f "$file" ]
then
shift
$file "$@"
else
echo "Service not found: $1"
fi

11
unprocessed/termux/scripts/sudo Executable file
View file

@ -0,0 +1,11 @@
#!/data/data/com.termux/files/usr/bin/bash
#
# Substitution for sudo
#
if [ "$(whoami)" != 'root' ]
then
echo "$@" | tsu
else
"$@"
fi

View file

@ -0,0 +1,6 @@
#!/data/data/com.termux/files/usr/bin/env bash
# Force the passing of the environment variables on LineageOS where the --preserve-environment
# option on the su binary doesn't seem to work well
/data/data/com.termux/files/usr/bin/tsu -s "$(env | sed "s/^\([^=]\+\)=\(.*\)/\1='\2'/" | tr '\n' ' ') $(which bash)"

3
unprocessed/termux/scripts/yt Executable file
View file

@ -0,0 +1,3 @@
#!/data/data/com.termux/files/usr/bin/env bash
cd ~/storage/shared/Movies/NewPipe/
youtube-dl --all-subs "${@: -1}"

View file

@ -0,0 +1,59 @@
#!/data/data/com.termux/files/usr/bin/bash
#
# Charge services based on phone state
#
PIDFILE="$HOME/.local/run/autosvc.pid"
LOGFILE="$HOME/.local/log/autosvc.log"
start() {
printf "Starting autosvc: "
start-stop-daemon -p "$PIDFILE" -x /data/data/com.termux/files/usr/bin/bash -S -b -m -- "$HOME/.termux/scripts/autosvc" -d -l "$LOGFILE"
echo "OK"
}
stop() {
printf "Stopping autosvc: "
start-stop-daemon -p "$PIDFILE" -x /data/data/com.termux/files/usr/bin/bash -K
echo "OK"
}
status() {
printf "autosvc: "
PID="$(cat "$PIDFILE" 2> /dev/null)"
if [[ -n "$PID" && -d "/proc/$PID" ]]
then
echo "running"
else
echo "stopped"
fi
}
log() {
tail "$@" "$LOGFILE"
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart|reload)
stop
start
;;
status)
status
;;
log)
shift
log "$@"
;;
*)
echo "Usage: $0 {start|stop|restart|status|log}"
exit 1
esac
exit $?

View file

@ -0,0 +1,59 @@
#!/data/data/com.termux/files/usr/bin/bash
#
# Start crond
#
PIDFILE="$HOME/.local/run/crond.pid"
LOGFILE="$HOME/.local/log/crond.log"
start() {
printf "Starting crond: "
start-stop-daemon -p "$PIDFILE" -x crond -S -b -m -- -f -L "$LOGFILE"
echo "OK"
}
stop() {
printf "Stopping crond: "
start-stop-daemon -p "$PIDFILE" -x crond -K
echo "OK"
}
status() {
printf "crond: "
PID="$(cat "$PIDFILE" 2> /dev/null)"
if [[ -n "$PID" && -d "/proc/$PID" ]]
then
echo "running"
else
echo "stopped"
fi
}
log() {
tail "$@" "$LOGFILE"
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart|reload)
stop
start
;;
status)
status
;;
log)
shift
log "$@"
;;
*)
echo "Usage: $0 {start|stop|restart|status|log}"
exit 1
esac
exit $?

View file

@ -0,0 +1,59 @@
#!/data/data/com.termux/files/usr/bin/bash
#
# Start SSH server daemon
#
PIDFILE="/data/data/com.termux/files/usr/var/run/sshd.pid"
LOGFILE="$HOME/.local/log/sshd.log"
start() {
printf "Starting SSHD: "
start-stop-daemon -p "$PIDFILE" -x sshd -S -- -E "$LOGFILE"
echo "OK"
}
stop() {
printf "Stopping SSHD: "
start-stop-daemon -p "$PIDFILE" -x sshd -K
echo "OK"
}
status() {
printf "SSHD: "
PID="$(cat "$PIDFILE" 2> /dev/null)"
if [[ -n "$PID" && -d "/proc/$PID" ]]
then
echo "running"
else
echo "stopped"
fi
}
log() {
tail "$@" "$LOGFILE"
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart|reload)
stop
start
;;
status)
status
;;
log)
shift
log "$@"
;;
*)
echo "Usage: $0 {start|stop|restart|status|log}"
exit 1
esac
exit $?

View file

@ -0,0 +1,59 @@
#!/data/data/com.termux/files/usr/bin/bash
#
# Start Syncthing synchronization service
#
PIDFILE="$HOME/.local/run/syncthing.pid"
LOGFILE="$HOME/.local/log/syncthing.log"
start() {
printf "Starting Syncthing: "
sudo start-stop-daemon -p "$PIDFILE" -x syncthing -S -b -N 5 -m -- -logfile="$LOGFILE" -home ~/.config/syncthing
echo "OK"
}
stop() {
printf "Stopping Syncthing: "
sudo start-stop-daemon -p "$PIDFILE" -x syncthing -K
echo "OK"
}
status() {
printf "Syncthing: "
PID="$(sudo cat "$PIDFILE" 2> /dev/null)"
if [[ -n "$PID" && -d "/proc/$PID" ]]
then
echo "running"
else
echo "stopped"
fi
}
log() {
sudo tail "$@" "$LOGFILE"
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart|reload)
stop
start
;;
status)
status
;;
log)
shift
log "$@"
;;
*)
echo "Usage: $0 {start|stop|restart|status|log}"
exit 1
esac
exit $?

View file

@ -0,0 +1,59 @@
#!/data/data/com.termux/files/usr/bin/bash
#
# Start Syncthing synchronization service
#
PIDFILE="$HOME/.local/run/syncthing.pid"
LOGFILE="$HOME/.local/log/syncthing.log"
start() {
printf "Starting Syncthing: "
start-stop-daemon -p "$PIDFILE" -x syncthing -S -b -N 5 -m -- -logfile="$LOGFILE" -home ~/.config/syncthing
echo "OK"
}
stop() {
printf "Stopping Syncthing: "
start-stop-daemon -p "$PIDFILE" -x syncthing -K
echo "OK"
}
status() {
printf "Syncthing: "
PID="$(cat "$PIDFILE" 2> /dev/null)"
if [[ -n "$PID" && -d "/proc/$PID" ]]
then
echo "running"
else
echo "stopped"
fi
}
log() {
tail "$@" "$LOGFILE"
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart|reload)
stop
start
;;
status)
status
;;
log)
shift
log "$@"
;;
*)
echo "Usage: $0 {start|stop|restart|status|log}"
exit 1
esac
exit $?

1
unprocessed/termux/shell Symbolic link
View file

@ -0,0 +1 @@
/data/data/com.termux/files/usr/bin/zsh

15
unprocessed/xsession Executable file
View file

@ -0,0 +1,15 @@
#!/bin/sh
#
# ~/.xsession
#
# Sourced by display managers
#
[ -f ~/.xprofile ] && . ~/.xprofile
if [ -f ~/.config/override_dm_choice ]
then
. ~/.config/xinitrc
else
. ~/.config/xinitrc $@
fi