summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Sima <ben@bsima.me>2018-06-13 21:36:56 -0700
committerBen Sima <ben@bsima.me>2018-06-13 21:36:56 -0700
commit77c9a177b2b595d4ce25095b58e2388fe33cc97a (patch)
tree0853d5c92d67538760005b9c4635a90115bd7ba4
init
-rwxr-xr-xburn-rate50
-rwxr-xr-xcheckbattery15
-rwxr-xr-xcheckmail7
-rwxr-xr-xcheckpodcasts12
-rwxr-xr-xcheckvpn16
-rwxr-xr-xclear-desk3
-rwxr-xr-xconnect-liaison-vpn.sh13
-rwxr-xr-xcptorrents18
-rwxr-xr-xeml15
-rwxr-xr-xfix-journal-filenames.py21
-rwxr-xr-xgetsong8
-rwxr-xr-xglyph119
-rwxr-xr-xinstall-roswell.sh17
-rwxr-xr-xirssi-tls20
-rwxr-xr-xjournal-reminder29
-rwxr-xr-xjournal-reminder.ros36
-rwxr-xr-xkb-light38
-rwxr-xr-xliaison-startup3
-rwxr-xr-xpreprocess-ynab-export51
-rwxr-xr-xrdr5
-rwxr-xr-xroun27
-rwxr-xr-xseeme3
-rwxr-xr-xsimple136
-rwxr-xr-xstart-emacs4
-rwxr-xr-xstart-redshift12
-rwxr-xr-xstop-emacs3
-rwxr-xr-xstop-redshift3
-rwxr-xr-xsun32
-rwxr-xr-xterminal-colours.sh46
-rwxr-xr-xtextract19
-rwxr-xr-xtunnel-monit6
-rwxr-xr-xtunnel-znc4
-rwxr-xr-xupdate-bnet3
-rwxr-xr-xvlog14
-rwxr-xr-xworkon34
-rwxr-xr-xworkreminder49
36 files changed, 891 insertions, 0 deletions
diff --git a/burn-rate b/burn-rate
new file mode 100755
index 0000000..5d79481
--- /dev/null
+++ b/burn-rate
@@ -0,0 +1,50 @@
+#!/usr/bin/env racket
+#lang racket/base
+
+;; burn-rate :: A little script to calculate my burn rate.
+;;
+;; Author: Ben Sima <bensima@gmail.com>
+;; License: MIT
+
+(require
+ (only-in racket/cmdline command-line))
+
+(define (burn safe rate days)
+ (- safe (* rate days)))
+
+(define (round-off z n) ; http://stackoverflow.com/a/16302176
+ (let ((power (expt 10 n)))
+ (/ (round (* power z)) power)))
+
+(define (burn-fmt amount)
+ (format "You have $~a remaining this pay period!\n" (round-off amount 2)))
+
+;;; CLI
+
+(define safe-to-spend 0)
+(define burn-rate 0)
+(define days-left 0)
+
+
+(define main
+ (command-line
+ #:usage-help
+ "Calculates your current burn rate, according to your Simple.com"
+ "account balances and days until your next paycheck."
+ #:once-each
+ [("-s" "--safe-to-spend") s "The amount left in Safe-to-Spend"
+ (set! safe-to-spend (string->number s))]
+
+ [("-b" "--burn-rate") b "How much you spend each day."
+ (set! burn-rate (string->number b))]
+
+ [("-d" "--days-left") d "How many days until next payheck."
+ (set! days-left (string->number d))]
+
+ #:multi
+ [("-o" "--offsets") o ("Additional money you know you have coming in later. Will simply be added to safe-to-spend.")
+ (set! safe-to-spend (+ safe-to-spend (string->number o)))]
+ #:args args
+ (if (= 0 (length args))
+ (printf "burn-rate :: A little script to calculate burn rate. Run with --help for instructions.\n")
+ (printf (burn-fmt (burn safe-to-spend burn-rate days-left))))))
diff --git a/checkbattery b/checkbattery
new file mode 100755
index 0000000..5b4cfe6
--- /dev/null
+++ b/checkbattery
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+export DISPLAY=:0
+
+BATT=`acpi -b | grep -P -o '[0-9]+(?=%)'`
+TIME=`acpi -b | grep -P -o '[0-9]{2}:[0-9]{2}:[0-9]{2}'`
+
+if (( $BATT < 10 ))
+then
+ notify-send -u critical "LOW BATTERY!" "\
+Battery Charge: ${BATT}%
+Time Remaining: ${TIME}"
+fi
+
+exit 0
diff --git a/checkmail b/checkmail
new file mode 100755
index 0000000..9ab4377
--- /dev/null
+++ b/checkmail
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+
+export DISPLAY=:0
+
+echo "$(date +%Y.%m.%d..%H.%M) $(basename $0)" >> /var/log/ben.cron.log
+/usr/bin/offlineimap
+/home/ben/me/bin/eml -n
diff --git a/checkpodcasts b/checkpodcasts
new file mode 100755
index 0000000..ec40440
--- /dev/null
+++ b/checkpodcasts
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+export DISPLAY=:0
+
+export cfg=~/.config/puckfetcher
+
+puckfetcher -c $cfg -d ~/Podcasts -a ~/tmp/podcasts update
+
+export ret=$(puckfetcher -c $cfg -d ~/Podcasts -a ~/tmp/podcasts list)
+echo "$(date +%Y.%m.%d..%H.%M) $(basename $0): $ret" \
+ >> /var/log/ben.cron.log
+notify-send "Podcasts updated!" "$ret"
diff --git a/checkvpn b/checkvpn
new file mode 100755
index 0000000..fcc0290
--- /dev/null
+++ b/checkvpn
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+export DISPLAY=:0
+
+
+export VPN=$(nmcli con show --active | grep vpn | cut -d" " -f1)
+
+echo $VPN
+
+if [ -z $VPN ]
+then
+ notify-send \
+ -t 10000 \
+ -u critical "No VPN connected" \
+ "Connect that shit or the CIA will get ya"
+fi
diff --git a/clear-desk b/clear-desk
new file mode 100755
index 0000000..426d1cc
--- /dev/null
+++ b/clear-desk
@@ -0,0 +1,3 @@
+#!/usr/bin/env sh
+
+[ -d ~/desk ] && rm -rf ~/desk/*
diff --git a/connect-liaison-vpn.sh b/connect-liaison-vpn.sh
new file mode 100755
index 0000000..b6fb931
--- /dev/null
+++ b/connect-liaison-vpn.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env sh
+
+conn=$(nmcli con show --active|grep liaison)
+
+export DISPLAY=:0
+
+if [ -n "$conn" ]; then
+ exit 0
+else
+ notify-send -t 3000 "Reconnecting to Liaison VPN..."
+ nmcli con up liaison
+ exit 0
+fi
diff --git a/cptorrents b/cptorrents
new file mode 100755
index 0000000..a1f9415
--- /dev/null
+++ b/cptorrents
@@ -0,0 +1,18 @@
+#!/usr/bin/env sh
+
+files=$(ls ~/Downloads/*.torrent)
+
+if [ -n "$files" ]; then
+ printf "selected torrents:\n$files\n\n"
+ read -p "ok? [Y/n] " reply
+ case $reply in
+ n | N | no | NO ) exit 1;;
+ *) printf "syncing\n";
+ rsync -av ~/Downloads/*.torrent ben@$(cat ~/me/data/home-ip):/mnt/lake/ben/torrents/in;
+ rm ~/Downloads/*.torrent;
+ exit 0;
+ esac
+else
+ echo "No torrents found."
+ exit 1;
+fi
diff --git a/eml b/eml
new file mode 100755
index 0000000..a480d18
--- /dev/null
+++ b/eml
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+
+eml(){
+ maildirs="$HOME/Mail/*/INBOX/new/"
+ find $maildirs -type f | wc -l
+}
+
+case "$1" in
+ -n) m=$(eml)
+ if ((m > 0)); then
+ notify-send "New mail" "$m messages"
+ fi
+ ;;
+ *) eml ;;
+esac
diff --git a/fix-journal-filenames.py b/fix-journal-filenames.py
new file mode 100755
index 0000000..5784f40
--- /dev/null
+++ b/fix-journal-filenames.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python3
+
+import os
+import re
+
+year_re = r"(^\d{4})\d{2}\d{2}$"
+month_re = r"^\d{4}(\d{2})\d{2}$"
+day_re = r"^\d{4}\d{2}(\d{2}$)"
+
+files = os.listdir(os.path.expanduser("~/me/journal"))
+for f in files:
+ print("working: ", f)
+ if '.' in f:
+ continue
+ year = re.findall(year_re, f)[0]
+ month = re.findall(month_re, f)[0]
+ day = re.findall(day_re, f)[0]
+ os.rename(
+ "/home/ben/me/journal/{0}".format(f),
+ "/home/ben/me/journal/{0}.{1}.{2}".format(year, month, day)
+ )
diff --git a/getsong b/getsong
new file mode 100755
index 0000000..a7f5a86
--- /dev/null
+++ b/getsong
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+youtube-dl \
+ -x \
+ --audio-format mp3 \
+ --audio-quality 0 \
+ --exec 'beet import' \
+ $@ \ No newline at end of file
diff --git a/glyph b/glyph
new file mode 100755
index 0000000..54c28e5
--- /dev/null
+++ b/glyph
@@ -0,0 +1,119 @@
+#!/usr/bin/env racket
+#lang racket/base
+
+;; glyph :: Convert between glyphs and their pronunciation.
+;;
+;; see http://urbit.org/docs/dev/hoon/leap-in/1-basic#-how-to-form-expressions
+;;
+;; Author: Ben Sima <bensima@gmail.com>
+;; License: MIT
+
+(require
+ (only-in racket/cmdline command-line)
+ (only-in racket/format ~a ~v)
+ (only-in racket/string string-join)
+ (only-in racket/dict dict-has-key?)
+ (only-in racket/list empty? take drop))
+
+;;; Dictionary and conversion functions
+
+(define glyphs
+ '((ace . #\space )
+ (gal . #\<)
+ (pel . #\()
+ (bar . #\|)
+ (gap . #\newline)
+ (gap . #\tab)
+ (per . #\))
+ (bas . #\\)
+ (gar . #\>)
+ (sel . #\[)
+ (buc . #\$)
+ (hax . #\#)
+ (sem . #\;)
+ (cab . #\_)
+ (hep . #\-)
+ (ser . #\])
+ (cen . #\%)
+ (kel . #\{)
+ (soq . #\')
+ (col . #\:)
+ (ker . #\})
+ (tar . #\*)
+ (com . #\,)
+ (ket . #\^)
+ (tec . #\`)
+ (doq . #\")
+ (lus . #\+)
+ (tis . #\=)
+ (dot . #\.)
+ (pam . #\&)
+ (wut . #\?)
+ (fas . #\/)
+ (pat . #\@)
+ (sig . #\~)
+ (zap . #\!)))
+
+
+(define (not-found x) (format "Not found in glyph dictionary: ~a" x))
+
+
+(define (glyph->name glyph)
+ (let ((res (filter (lambda (pair) (eq? (cdr pair) glyph)) glyphs)))
+ (if (eq? '() res) ;could really use anaphora here
+ (not-found glyph)
+ (caar res))))
+
+
+(define (name->glyph name)
+ (if (dict-has-key? glyphs name)
+ (cdar (filter (lambda (pair) (eq? (car pair) name)) glyphs))
+ (not-found name)))
+
+
+;;; Table output functions
+
+(define (pair->row p)
+ (let* ((name (car p))
+ (glyph (cdr p)))
+ (display
+ (string-append (~a "| " name " |")
+ (~v glyph #:min-width 10 #:left-pad-string " " #:align 'right)
+ " |\n"
+ "|------------------|\n"))))
+
+(define (dict->table d)
+ (display "| Name | Glyph |\n")
+ (display "|------+-----------|\n")
+ (map pair->row d))
+
+
+;;; Input Parser
+
+(define (parse-glyphs s)
+ (map glyph->name (string->list s)))
+
+
+;;; Entrypoint
+
+(define main
+ (command-line
+
+ #:once-each
+ [("-a" "--all") "Print a table all the glyphs."
+ (dict->table glyphs)]
+
+ #:args input
+ (cond ((empty? input) ; return nothing if no input
+ null)
+
+ (else
+ (printf "~a~n"
+ (string-join
+ (map symbol->string
+ (parse-glyphs (car input)))))))))
+
+;; FIXME: I need to write my own CLI parser:
+;; ben@neb bin : glyph "++"
+;; glyph "++"
+;; glyph: unknown switch: ++ \ No newline at end of file
diff --git a/install-roswell.sh b/install-roswell.sh
new file mode 100755
index 0000000..40f6daa
--- /dev/null
+++ b/install-roswell.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env sh
+#
+# Dependencies:
+# - libcurl (both v3 and v4 should work) : used for downloading the lisp implimentation binaries etc.
+# - automake (required when building from the source)
+# - developmental headers of libcurl (required when building from the source)
+
+set -e
+set -x
+
+[ -d ~/cache ] && mkdir -p ~/cache
+git clone -b release https://github.com/snmsts/roswell.git ~/cache/roswell
+cd ~/cache/roswell
+sh bootstrap
+./configure
+make
+sudo make install
diff --git a/irssi-tls b/irssi-tls
new file mode 100755
index 0000000..50d4fdd
--- /dev/null
+++ b/irssi-tls
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+bsima_cert=/tmp/bsima.me.cert
+
+openssl s_client -connect bsima.me:6697 < /dev/null 2>/dev/null | \
+ openssl x509 > $bsima_cert
+
+case "$1" in
+ cert) openssl x509 -in $bsima_cert -fingerprint -sha256 -noout
+ ;;
+ pubkey) openssl x509 -in $bsima_cert -pubkey -noout | \
+ openssl pkey -pubin -outform der | \
+ openssl dgst -sha256 -c | \
+ tr a-z A-Z
+ ;;
+ *) echo "usage: $(basename $0) {cert,pubkey}"; exit 1
+ ;;
+esac
+
+
diff --git a/journal-reminder b/journal-reminder
new file mode 100755
index 0000000..82e188f
--- /dev/null
+++ b/journal-reminder
@@ -0,0 +1,29 @@
+#!/usr/bin/env racket
+#|-*- mode:scheme *-*|#
+#lang racket/base
+
+(require (only-in racket/cmdline command-line))
+
+(define +journal-dir+ "/home/ben/Dropbox/org/journal")
+
+(define (pad-zero n) (if (< n 10) (format "0~a" n) n))
+
+(define (today)
+ (let* [(date (seconds->date (current-seconds)))
+ (year (date-year date))
+ (month (date-month date))
+ (day (date-day date))]
+ (format "~a~a~a"
+ (date-year date)
+ (pad-zero month)
+ (pad-zero day))))
+
+(define (journal-file d)
+ (format "~a/~a" +journal-dir+ d))
+
+(define (journaled-today? d)
+ (if (file-exists? (journal-file d)) #t #f))
+
+(if (not (journaled-today? (today)))
+ "You still need to journal!"
+ null)
diff --git a/journal-reminder.ros b/journal-reminder.ros
new file mode 100755
index 0000000..500be53
--- /dev/null
+++ b/journal-reminder.ros
@@ -0,0 +1,36 @@
+#!/bin/sh
+#|-*- mode:lisp -*-|#
+#|
+exec ros -Q -- $0 "$@"
+|#
+
+(defpackage :me.bsima.journal-reminder (:use :cl))
+(in-package :me.bsima.journal-reminder)
+
+(defparameter *journal-dir* "/home/ben/Dropbox/org/journal")
+
+(defun today-string ()
+ (multiple-value-bind (second minute hour day month year)
+ (get-decoded-time)
+ (declare (ignore second minute hour))
+ (format nil "~d~d~d" year month day)))
+
+(defun mk-journal-file (journal-dir date)
+ (format nil "~a/~a" journal-dir date))
+
+(defun journaled-today? ()
+ (let ((journal-file (mk-journal-file *journal-dir* (today-string))))
+ (if (probe-file journal-file)
+ T
+ nil)))
+
+(defun journal-reminder (&rest argv)
+ (declare (ignorable argv))
+ ;; If journal-dir argumen is supplied, use it
+ (let ((*journal-dir* (or (first argv) *journal-dir*)))
+ (if (journaled-today?)
+ nil
+ (format t "You still need to journal!~%"))))
+
+(defun main (&rest argv)
+ (apply #'journal-reminder argv))
diff --git a/kb-light b/kb-light
new file mode 100755
index 0000000..a13f1fa
--- /dev/null
+++ b/kb-light
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+
+import dbus
+import sys
+
+def kb_light_set(delta):
+ bus = dbus.SystemBus()
+ kbd_backlight_proxy = bus.get_object('org.freedesktop.UPower', '/org/freedesktop/UPower/KbdBacklight')
+ kbd_backlight = dbus.Interface(kbd_backlight_proxy, 'org.freedesktop.UPower.KbdBacklight')
+
+ current = kbd_backlight.GetBrightness()
+ maximum = kbd_backlight.GetMaxBrightness()
+ new = max(0, min(current + delta, maximum))
+
+ if 0 <= new <= maximum:
+ current = new
+ kbd_backlight.SetBrightness(current)
+
+ # Return current backlight level percentage
+ return 100 * current / maximum
+
+if __name__ == '__main__':
+ if len(sys.argv) == 2 or len(sys.argv) == 3:
+ if sys.argv[1] == "--up" or sys.argv[1] == "+":
+ if len(sys.argv) == 3:
+ print(kb_light_set(int(sys.argv[2])))
+ else:
+ print(kb_light_set(17))
+ elif sys.argv[1] == "--down" or sys.argv[1] == "-":
+ if len(sys.argv) == 3:
+ print(kb_light_set(-int(sys.argv[2])))
+ else:
+ print(kb_light_set(-17))
+ else:
+ print("Unknown argument:", sys.argv[1])
+ else:
+ print("Script takes one or two argument.", len(sys.argv) - 1, "arguments provided.")
+
diff --git a/liaison-startup b/liaison-startup
new file mode 100755
index 0000000..4300744
--- /dev/null
+++ b/liaison-startup
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+
+chromium --profile-directory="Profile 2"
diff --git a/preprocess-ynab-export b/preprocess-ynab-export
new file mode 100755
index 0000000..e0f0b7d
--- /dev/null
+++ b/preprocess-ynab-export
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+
+"""
+script must:
+- remove all 0.00 amounts
+- keep 0.00 amounts-in when Payee == "Starting Balance"
+"""
+
+import re
+import argparse
+
+# remove zeros and escaped quotes
+zeros = re.compile(',0[.]00,')
+escaped_quotes = re.compile('\"Last Temptation\"')
+
+def scrub(s):
+ if "Last Temptation" in s:
+ s = escaped_quotes.sub('', s)
+ elif "Starting Balance" in s:
+ s = zeros.sub(',,', s, count=1)
+ else:
+ s = zeros.sub(',,', s)
+
+ return s
+
+
+parser = argparse.ArgumentParser()
+parser.add_argument(
+ 'input', metavar='INPUT', type=str,
+ help='The input csv file, exported from YNAB'
+)
+parser.add_argument(
+ 'output', metavar='OUTPUT', type=str,
+ help='The output csv file.'
+)
+
+
+
+if __name__ == '__main__':
+ args = parser.parse_args()
+
+ with open(args.input, 'r') as f:
+ csv = f.readlines()
+
+ scrubbed = [scrub(line) for line in csv[1:]]
+ with open(args.output, 'w') as f:
+ for line in scrubbed:
+ f.write(line)
+
+
+
diff --git a/rdr b/rdr
new file mode 100755
index 0000000..d08ceaa
--- /dev/null
+++ b/rdr
@@ -0,0 +1,5 @@
+#!/usr/bin/env sh
+
+textract "$1" \
+ | pandoc -f html -t markdown \
+ | less
diff --git a/roun b/roun
new file mode 100755
index 0000000..54447dd
--- /dev/null
+++ b/roun
@@ -0,0 +1,27 @@
+#!/usr/bin/env racket
+#lang racket/base
+
+;; http://www.flipcode.com/archives/Generating_Names_Phonetically.shtml
+
+(define consonants '(#\b #\c #\d #\f #\g #\h #\j #\k #\l #\m #\n #\p #\q #\r #\s #\t #\v #\w #\x #\y #\z))
+(define c-len (length consonants))
+(define (rand-consonant) (list-ref consonants (random c-len)))
+
+(define vowels '(#\a #\e #\i #\o #\u))
+(define v-len (length vowels))
+(define (rand-vowel) (list-ref vowels (random v-len)))
+
+(define generate-roun
+ (lambda ()
+ (string
+ (rand-consonant) (rand-vowel) (rand-consonant))))
+
+(define *help*
+ "roun :: generates random consonant-vowel-consonant words")
+
+(let ((args (current-command-line-arguments)))
+ (cond
+ ((= 0 (vector-length args))
+ (printf "~a\n" (generate-roun)))
+
+ (else (printf "~a\n" *help*))))
diff --git a/seeme b/seeme
new file mode 100755
index 0000000..d9076e1
--- /dev/null
+++ b/seeme
@@ -0,0 +1,3 @@
+#!/usr/bin/env sh
+
+ffplay -f video4linux2 -i /dev/video0 -video_size 320x240 -noborder
diff --git a/simple b/simple
new file mode 100755
index 0000000..2a4098d
--- /dev/null
+++ b/simple
@@ -0,0 +1,136 @@
+#!/usr/bin/env python
+#
+# simple.py - analyze your Simple spending data
+#
+# A little script to calculate my burn rate. I use it with Simple.com :)
+#
+# Author: Ben Sima <bensima@gmail.com>
+# License: MIT
+#
+
+
+import os
+import json
+import click # pip install click
+import locale
+from decimal import *
+from pymonad import * # pip install pymonad
+from functools import *
+from datetime import datetime, date
+
+
+locale.setlocale(locale.LC_ALL, '')
+now = datetime.now()
+my_age = now.year - date(1992, now.month, now.day).year
+
+
+@click.group()
+@click.option('--debug/--no-debug', default=False)
+def cli(debug):
+ pass
+
+
+@cli.command()
+@click.option('-s', '--safe', prompt="Current Safe-to-Spend Resevoir", help="Safe to Spend")
+@click.option('-r', '--rate', prompt="Your rate of savings per day", help="Savings rate")
+@click.option('-d', '--days', prompt="Days until your next paycheck", help="Days until next paycheck")
+def burn(safe: Decimal, rate: Decimal, days: Decimal) -> Decimal:
+ """Calculates my burn rate based on current paycheck, current saving
+ amount, and days until next paycheck.
+ """
+ money = Decimal(safe) - (Decimal(rate) * Decimal(days))
+ color = "green" if money > 0 else "red"
+ msg = "You have {} ramaining this pay period!".format(click.style(locale.currency(money), fg = color))
+ click.echo(msg)
+
+
+@cli.command()
+@click.option('-a', '--age', help="[TODO] Your age. This will let me know how much time it will take before you can say 'fuck you'. Defaults to my age ({})".format(my_age))
+@click.option('-r', '--rate', help="[TODO] Your rate of savings per day. This helps calculate how long it will take to save up for retirement.")
+@click.option('-i', '--investments', help="[TODO] Total value of your outside investments that are not represented in your Simple account data.")
+@click.argument('simple_data', type=click.Path(exists=True))
+def fu(simple_data, age, rate, investments):
+ """A calculator for 'Fuck You Money', as explained by Humphrey Bogart:
+
+ > The only good reason to have money is this: so that you can tell
+ any SOB in the world to go to hell.
+
+ Reads your exported Simple data[0] and tells you if you're making
+ enough money or spending properly to retire young. Obviously this
+ is very contingent on other things, such as investments and other
+ accounts, etc. But for the most part, if you can save 25 times
+ your annual spending, then you can retire forever[1].
+
+ So basically, this command calculates how much you'll need to
+ save, based on your annual spending.
+
+ If you include your rate of savings (-r, how much money you
+ transfer into your dedicated retirement savings account per day)
+ then this will tell you how long it will take until you're set for
+ retirement.
+
+ If you also include existing investments (-i), then I'll factor
+ those in too.
+
+ If you include your age (-a), I'll tell you how old you'll be when
+ you can retire.
+
+ This algorithm does not take into account any changes in your
+ condition such as annual salary increases or saving for your kids'
+ college tuition, but it does make the same assumptions about the
+ economy as Mr. Money Mustache[1].
+
+ [0]: https://www.simple.com/help/articles/account-info/statements-and-export
+ [1]: http://www.mrmoneymustache.com/2012/05/29/how-much-do-i-need-for-retirement/
+
+ """
+ with open(simple_data) as data_file:
+ raw_data = json.load(data_file)
+
+ # pull out only the data I want
+ data = []
+ for tx in raw_data["transactions"]:
+ data.append({
+ "type": tx["bookkeeping_type"],
+ "uuid": tx["uuid"],
+ "amount": tx["amounts"]["amount"],
+ "date": datetime.strptime(tx["times"]["when_recorded_local"], "%Y-%m-%d %H:%M:%S.%f")
+ })
+
+ # now add up all the credits and debits from the past year
+ @curry
+ def yearp(y: int, x: datetime) -> bool:
+ return x.year == y
+
+ current_yearp = yearp(now.year)
+ active_years = set(map(lambda x: x["date"].year, data))
+
+ def amount_sum(x,y):
+ "Quick reducer for this nested data. Wish I could dispatch on type..."
+ if type(x) is dict:
+ return x["amount"] + y["amount"]
+ elif type(x) is int:
+ return x + y["amount"]
+
+ debits = {}
+ for year in active_years:
+ debits[str(year)] = {}
+ this = debits[str(year)]
+ this["transactions"] = list(filter(lambda x: yearp(year, x["date"]) and x["type"] == "debit", data))
+ this["total"] = reduce(amount_sum, this["transactions"])
+
+ current_annual_debits = debits[str(now.year)]["total"]
+ mean_annual_debits = reduce(lambda x,y: debits[x]["total"] + debits[y]["total"], debits) / len(debits.keys())
+ required_retirement_fund = mean_annual_debits * 25
+
+ # note that the amounts need to be divided by 10,000 to get back
+ # to normal monetary representations
+ display = lambda n: locale.currency(float(n / 10000), grouping=True)
+
+ click.echo("Your average annual spending is {}. At this rate, you'll need {} saved up before you can retire and say 'fuck you!' to the system, man."
+ .format(click.style(display(current_annual_debits), fg="green"),
+ click.style(display(required_retirement_fund), fg="green")))
+
+
+if __name__ == '__main__':
+ cli()
diff --git a/start-emacs b/start-emacs
new file mode 100755
index 0000000..439a63f
--- /dev/null
+++ b/start-emacs
@@ -0,0 +1,4 @@
+#!/usr/bin/env sh
+
+source ~/.nix-profile/etc/profile.d/nix.sh
+emacs --daemon \ No newline at end of file
diff --git a/start-redshift b/start-redshift
new file mode 100755
index 0000000..81b985d
--- /dev/null
+++ b/start-redshift
@@ -0,0 +1,12 @@
+#!/usr/bin/env sh
+
+set -o errexit
+set -o nounset
+#set -o xtrace
+
+REQ=$(curl -sS freegeoip.net/json/)
+LAT=$(echo $REQ | jq ".latitude")
+LON=$(echo $REQ | jq ."longitude")
+
+# start redshift as a background task
+redshift -l $LAT:$LON &
diff --git a/stop-emacs b/stop-emacs
new file mode 100755
index 0000000..f681d56
--- /dev/null
+++ b/stop-emacs
@@ -0,0 +1,3 @@
+#!/usr/bin/env sh
+
+emacsclient --eval "(progn (setq kill-emacs-hook 'nil) (kill-emacs))" \ No newline at end of file
diff --git a/stop-redshift b/stop-redshift
new file mode 100755
index 0000000..1d68b88
--- /dev/null
+++ b/stop-redshift
@@ -0,0 +1,3 @@
+#!/usr/bin/env sh
+
+pkill redshift \ No newline at end of file
diff --git a/sun b/sun
new file mode 100755
index 0000000..ada17ba
--- /dev/null
+++ b/sun
@@ -0,0 +1,32 @@
+#!/usr/bin/env perl
+
+use strict;
+use utf8;
+
+use DateTime;
+use DateTime::Event::Sunrise;
+
+
+# Location
+my $lat = "33.020833";
+my $lon = "-117.279167";
+
+
+# Current date
+my $now = DateTime->now;
+$now->set_time_zone("local");
+
+my $fmt = "%H:%M";
+
+# Get sunrise and sunset data
+my $sun = DateTime::Event::Sunrise->new (
+ longitude => $lon,
+ latitude => $lat,
+ precise => 1
+);
+
+print "Sunrise: ", $sun->sunrise_datetime($now)->strftime($fmt);
+print "\n";
+print "Sunset: ", $sun->sunset_datetime($now)->strftime($fmt);
+print "\n";
+
diff --git a/terminal-colours.sh b/terminal-colours.sh
new file mode 100755
index 0000000..dc423e5
--- /dev/null
+++ b/terminal-colours.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# A non-bash version of
+#
+# http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html
+#
+# Original author:
+# https://lobste.rs/s/uyw4pq/lobsters_battlestations_screenshots#c_5qrnht
+
+text=${1:-gYw}
+textwidth=$((${#text} < 3 ? 3 : ${#text}))
+
+printtable () {
+ printf '\n%8s' ' '
+ for bg in $(bgs); do
+ printcol '' $bg ''
+ done
+ printf '\n'
+ for fg in $(fgs); do
+ printrow $fg
+ printrow 1\;$fg
+ done
+ printf '\n'
+}
+
+printcol () {
+ printf "%s %${textwidth}s %s " "${@}"
+}
+
+printrow () {
+ printf '%6s ' "${1}"
+ for bg in $(bgs); do
+ printf "\033[%s\033[%s %${textwidth}s \033[0m " "${1}" $bg "${text}"
+ done
+ printf '\n'
+}
+
+bgs () {
+ (printf 49\\n; seq 40 47) | sed s/$/m/
+}
+
+fgs () {
+ (printf 39\\n; seq 30 37) | sed s/$/m/
+}
+
+printtable
diff --git a/textract b/textract
new file mode 100755
index 0000000..996393f
--- /dev/null
+++ b/textract
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3.5
+
+import argparse
+from readability import Document
+import requests
+import sys
+
+cli = argparse.ArgumentParser('read a url')
+cli.add_argument('url', type=str)
+
+if __name__ == '__main__':
+ try:
+ args = cli.parse_args()
+ resp = requests.get(args.url)
+ doc = Document(resp.text)
+ sys.stdout.write(doc.summary())
+ sys.exit(0)
+ except:
+ sys.exit(1)
diff --git a/tunnel-monit b/tunnel-monit
new file mode 100755
index 0000000..505b83d
--- /dev/null
+++ b/tunnel-monit
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+PORT=2812
+
+echo "monit will be available at http://localhost:$PORT"
+ssh deploy@bsima.me -L $PORT:localhost:$PORT
diff --git a/tunnel-znc b/tunnel-znc
new file mode 100755
index 0000000..c6ac438
--- /dev/null
+++ b/tunnel-znc
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+echo "znc will be available at http://localhost:6643"
+ssh deploy@bsima.me -L 6643:localhost:6643
diff --git a/update-bnet b/update-bnet
new file mode 100755
index 0000000..30dc3b5
--- /dev/null
+++ b/update-bnet
@@ -0,0 +1,3 @@
+#!/usr/bin/env sh
+
+ssh bsima.me sudo apt upgrade -y \ No newline at end of file
diff --git a/vlog b/vlog
new file mode 100755
index 0000000..6a35a79
--- /dev/null
+++ b/vlog
@@ -0,0 +1,14 @@
+#!/usr/bin/env sh
+#
+# https://gist.github.com/dpacmittal/9f0e1aea8a3732d8f7aec7b1dadc8cda
+
+FILE=$(date +%Y.%m.%d..%H.%M)
+
+ffmpeg \
+ -f alsa -i default \
+ -f x11grab -video_size `xdpyinfo | grep 'dimensions:'|awk '{print $2}'` -framerate 25 -i :0.0 \
+ -f video4linux2 -i /dev/video0 \
+ -filter_complex '[2:v]scale=480:-1[cam];[1:v][cam]overlay=W-w-10:H-h-10' \
+ -ab 192 -acodec pcm_s16le -qscale 0 \
+ -c:v libx264 -crf:v 18 -c:a flac \
+ $HOME/Videos/vlog/$FILE.mkv
diff --git a/workon b/workon
new file mode 100755
index 0000000..42efb77
--- /dev/null
+++ b/workon
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+#
+# workon :: clones a git repo to ~/desk/<repo-name> and sets the remotes
+# appropriately
+#
+# Original author: https://github.com/sumitsu
+
+GITHUB_USER=bsima
+GITHUB_WORK=LiaisonTechnologies
+DESK="${HOME}/desk" # or wherever you typically put your code
+
+for REPO in "$@"
+do
+ echo "${REPO}";
+ if [ ! -d "${DESK}/${REPO}" ]
+ then
+ if ( ! git clone git@github.com:${GITHUB_USER}/${REPO}.git ${DESK}/${REPO} )
+ then
+ echo "FAILED";
+ exit 1;
+ fi;
+ else
+ echo "(already exists)";
+ exit 1;
+ fi;
+ cd "${DESK}/${REPO}";
+ git remote add upstream git@github.com:${GITHUB_WORK}/${REPO}.git;
+ git config branch.master.remote upstream
+ git config branch.master.merge refs/heads/master
+ git config remote.pushDefault origin
+ git remote -v;
+done;
+
+exit 0;
diff --git a/workreminder b/workreminder
new file mode 100755
index 0000000..5c9dcca
--- /dev/null
+++ b/workreminder
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+
+export DISPLAY=:0
+
+n=$(shuf -i 0-10 -n 1)
+
+case $n in
+ 0) notify-send -u low "Ask yourself:" "Am I being productive, or making \
+busy work?" ;;
+ 1) notify-send -u low "Ask yourself:" "Is what I'm doing now making money,\
+ or just spinning wheels?" ;;
+ 2) notify-send -u low "Remember:" \
+"Concentrate every minute like a Roman - like a man - on doing \
+what's in front of you with precise and genuine seriousness, \
+tenderly, willingly, with justice. And on freeing yourself from \
+all other distractions. Yes, you can - if you do everything as if \
+it were the last thing you were doing in your life, and stop \
+being aimless, stop letting your emotions override what your mind \
+tells you, stop being hypocritical, self-centered, irritable. You \
+see how few things you have to do to live a satisfying and \
+reverent life? If you can manage this, that's all even the gods \
+can ask of you."
+ ;;
+ 3) notify-send -u low "How to act:" "Never under compulsion, out of \
+selfishness, without forethought, with misgivings."
+ ;;
+ 4) notify-send -u low "How to act:" "Don't gussy up your thoughts." ;;
+ 5) notify-send -u low "How to act:" \
+ "No surplus words or unnecessary actions." ;;
+ 6) notify-send -u low "How to act:" \
+ "To stand up straight - not straightened." ;;
+ 7) notify-send -u low "Forget everythin else." \
+"Keep hold of this alone and remember it: Each of us lives only now, \
+this brief instant. The rest has been lived already, or is impossible \
+to see. The span we live is small - small as the corner of the earth \
+in which we live it. Small as even the greatest renown, passed from \
+mouth to mouth by short-lived stick figures, ignorant alike of \
+themselves and those long dead."
+ ;;
+ 8) notify-send -u low "Practice really hearing what people say. Do your \
+best to get inside their minds." ;;
+ 9) notify-send -u low "Perfection of character:" "to live your last day, \
+every day, without frenzy, or sloth, or pretense."
+ ;;
+ 10) notify-send -u low "It stares you in the face." \
+ "No role is so well suited to philosophy as the one you happen to be in \
+right now."
+ ;;
+esac