Skip to content

Instantly share code, notes, and snippets.

@iaindooley
iaindooley / worktree.sh
Created July 7, 2026 21:01
worktree script for tmux
#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
usage:
worktree [worktree-name]
worktree --headless worktree-name
worktree --undo [branch-name]
worktree --headless --undo branch-name
@iaindooley
iaindooley / MablePdfBudgetSummaryTracker
Created May 14, 2026 04:27
Mable PDF Budget Summary Tracker
/***** Mable Invoice / NDIS Budget Tracker for Google Sheets *****/
/*
Paste this whole file into Extensions > Apps Script.
Run initMableInvoiceTracker() once from the Apps Script editor.
This version uses the Gemini API to extract PDF invoice transactions directly from the PDF.
It does NOT try to create Gmail filters.
*/
const MABLE = {
@iaindooley
iaindooley / mable_budget_tracker.gs
Created May 12, 2026 11:55
Mable Budget Tracker
/***** Mable / NDIS Budget Tracker for Google Sheets *****
* Paste this whole file into Extensions > Apps Script.
* Run initMableTracker() once from the Apps Script editor.
*/
const TRACKER = {
sheets: {
summary: 'Summary',
transactions: 'Transactions',
config: 'Config',
@iaindooley
iaindooley / README.txt
Created February 20, 2025 00:23
Using GPG to encrypt documents on a USB
README - Secure Encrypted Backup Storage
This USB contains an encrypted backup of your 2FA recovery codes. The backup is stored as a GPG-encrypted tarball for maximum security and portability. Follow the instructions below to encrypt, decrypt, and format additional USB drives.
---
ENCRYPTING BACKUP DIRECTORY (macOS/Linux)
To encrypt a directory (my_backup_dir) and store it as backup.tar.gpg on this USB:
#Contents of encrypt.sh below
@iaindooley
iaindooley / totp.js
Created October 16, 2024 05:22
Personal TOTP URL Generator
function generateTOTPURL(secret, label,issuer) {
return `otpauth://totp/${label}?secret=${secret}&issuer=${issuer}`;
}
// Function to encode a string to Base32
function base32Encode(str) {
const base32Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
let binString = '';
for (let i = 0; i < str.length; i++) {
const charCode = str.charCodeAt(i);
@iaindooley
iaindooley / gist:51e75dd7315c889fec0f37de3e091aa1
Created August 23, 2022 04:12
Init trellinator submodules for a BenkoBot repo
git submodule add git@github.com:iaindooley/benko-board-trellinator.git apps/benko-board-trellinator
git submodule add git@github.com:iaindooley/trellinator-libs.git apps/trellinator-libs
git submodule add git@github.com:iaindooley/trellinator.git apps/trellinator
git submodule init
git submodule update
var notif = new Notification(notification);
var moved = notif.movedCard();
if(!notif.member().notTrellinator())//ignore moves made by the bot
throw new InvalidActionException("Don't loop!");
moved.cardsLinkedInAttachments().each(function(card)
{
card.moveToList(moved.currentList(),"bottom");
});
var notif = new Notification(notification);
var added = notif.addedChecklistItem();
if(!notif.member().notTrellinator())//don't react if the bot added the item
throw new InvalidActionException("Don't loop!");
var added_to = added.checklist();
var source_card = added_to.card();
card.board().cards().each(function(card)
@iaindooley
iaindooley / gist:99ff3d25f869e2b9d4311f692f888147
Created October 5, 2021 23:57
createWorkspaceAddMembers
var members = [
'username1',
'username2',
'username3'
];
var target = new Trellinator().team("My New Workspace");//find or create
for(var i = 0;i < members.length;i++)
{
var added = new Notification(notification).addedLabel("Copy With State");
var copy = added.card().copyToList(
added.card().board().findOrCreateList("Copied Cards"),
"top"
)
.markDueDateIncomplete();
added.card().checklists().each(function(cl)
{
cl.items().each(function(item)