#!/bin/bash # Birthday & Gift Tracker # Tracks family birthdays, sends reminders, suggests gifts # Checks daily at 9:00 AM set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DATA_FILE="$SCRIPT_DIR/birthdays.json" LOG_FILE="$SCRIPT_DIR/reminder-log.json" source "$SCRIPT_DIR/../../.env" 2>/dev/null || true TELEGRAM_CHAT="${TELEGRAM_CHAT:-1793951355}" log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" } # Initialize data file if not exists init_data() { if [[ ! -f "$DATA_FILE" ]]; then log "Creating birthday database..." cat > "$DATA_FILE" << 'EOF' { "people": [ { "name": "Grace Martin", "relationship": "Mum", "birthday": "06-02", "birth_year": 1951, "notes": "Cancer treatment ongoing - be extra thoughtful. Loves gardening, cooking, family time.", "gift_ideas": ["Flowers", "Gardening supplies", "Photo album", "Day spa voucher", "Home-cooked meal"], "past_gifts": [] }, { "name": "Harvey Martin", "relationship": "Dad", "birthday": "12-08", "birth_year": 1949, "notes": "Full-time carer for Grace. Needs respite/support. Loves tech gadgets, wine, coffee.", "gift_ideas": ["Ember mug", "Wine subscription", "Coffee beans", "Tech gadget", "Day out voucher"], "past_gifts": [] }, { "name": "Elizabeth Martin", "relationship": "Sister", "birthday": "09-11", "birth_year": 1990, "notes": "Vegan - avoid food gifts unless specifically vegan. Creative, environmentally conscious.", "gift_ideas": ["Vegan cookbook", "Eco-friendly products", "Plants", "Art supplies", "Experience gift"], "past_gifts": [] }, { "name": "Alexander", "relationship": "Godson/Cousin", "birthday": "07-XX", "birth_year": 2016, "notes": "8 years old (born July 2016). Loves games, books, LEGO, sports.", "gift_ideas": ["LEGO set", "Books", "Board games", "Sports equipment", "Science kit"], "past_gifts": [] }, { "name": "Mia Martin", "relationship": "Doggo šŸ•", "birthday": "XX-XX", "notes": "14 years old, beloved geriatric girl. Treats, toys, comfy beds.", "gift_ideas": ["Premium dog treats", "Comfy bed", "New toy", "Grooming session"], "past_gifts": [] } ], "settings": { "reminder_weeks_before": 2, "reminder_days_before": 7, "reminder_day_of": true } } EOF fi if [[ ! -f "$LOG_FILE" ]]; then echo '{"sent_reminders": []}' > "$LOG_FILE" fi } # Get today's date in MM-DD format today_mmdd() { date +%m-%d } # Get today's date in YYYY-MM-DD format today_yyyymmdd() { date +%Y-%m-%d } # Calculate days until birthday days_until_birthday() { local bday_mmdd="$1" local today=$(today_yyyymmdd) local current_year=$(date +%Y) # Handle missing day (XX) if [[ "$bday_mmdd" == *"XX"* ]]; then echo "unknown" return fi local bday_this_year="$current_year-$bday_mmdd" local bday_ts=$(date -d "$bday_this_year" +%s 2>/dev/null || echo "0") local today_ts=$(date -d "$today" +%s) # If birthday passed this year, calculate for next year if ((bday_ts < today_ts)); then ((current_year++)) bday_this_year="$current_year-$bday_mmdd" bday_ts=$(date -d "$bday_this_year" +%s) fi local diff=$(( (bday_ts - today_ts) / 86400 )) echo "$diff" } # Check if reminder already sent today reminder_sent() { local person="$1" local type="$2" local today=$(today_yyyymmdd) grep -q "\"$today-$person-$type\"" "$LOG_FILE" 2>/dev/null } # Log that reminder was sent log_reminder() { local person="$1" local type="$2" local today=$(today_yyyymmdd) # Add to log local temp_file=$(mktemp) jq --arg key "$today-$person-$type" '.sent_reminders += [$key]' "$LOG_FILE" > "$temp_file" mv "$temp_file" "$LOG_FILE" } # Clean old reminders from log (keep last 90 days) cleanup_log() { local cutoff=$(date -d "90 days ago" +%Y-%m-%d) local temp_file=$(mktemp) jq --arg cutoff "$cutoff" '.sent_reminders |= map(select(. > $cutoff or length < 10))' "$LOG_FILE" > "$temp_file" 2>/dev/null || true mv "$temp_file" "$LOG_FILE" 2>/dev/null || true } # Send Telegram message send_telegram() { local message="$1" curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \ -H "Content-Type: application/json" \ -d "{ \"chat_id\": \"$TELEGRAM_CHAT\", \"text\": \"$message\", \"parse_mode\": \"Markdown\" }" > /dev/null || { log "ERROR: Failed to send Telegram message" return 1 } } # Generate gift suggestions text gift_suggestions() { local ideas=$(echo "$1" | jq -r '.gift_ideas | join(", ")') local notes=$(echo "$1" | jq -r '.notes') local past=$(echo "$1" | jq -r '.past_gifts | if length > 0 then "Past gifts: " + join(", ") else "" end') local text="šŸ’” *Gift Ideas:* $ideas" if [[ -n "$past" && "$past" != "Past gifts: " ]]; then text+="\nšŸŽ $past" fi text+="\nšŸ“ *Notes:* $notes" echo "$text" } # Check birthdays and send reminders check_birthdays() { log "Checking birthdays..." local people=$(jq -c '.people[]' "$DATA_FILE") local settings=$(jq '.settings' "$DATA_FILE") local weeks_before=$(echo "$settings" | jq -r '.reminder_weeks_before') local days_before=$(echo "$settings" | jq -r '.reminder_days_before') local reminders_sent=0 while IFS= read -r person; do local name=$(echo "$person" | jq -r '.name') local relationship=$(echo "$person" | jq -r '.relationship') local birthday=$(echo "$person" | jq -r '.birthday') local birth_year=$(echo "$person" | jq -r '.birth_year // empty') # Skip if no specific date if [[ "$birthday" == *"XX"* ]]; then continue fi local days_until=$(days_until_birthday "$birthday") # Calculate age if birth year known local age_text="" if [[ -n "$birth_year" && "$birth_year" != "null" ]]; then local current_year=$(date +%Y) local age=$((current_year - birth_year)) if ((days_until < 365)); then age_text=" (turning $age)" fi fi # Check reminders local should_remind=false local reminder_type="" local message="" if ((days_until == 0)); then reminder_type="today" if ! reminder_sent "$name" "$reminder_type"; then message="šŸŽ‚ *Birthday Today!*\n\n*$name* — your $relationship$age_text\n\nDon't forget to call/message them! šŸ’" local gifts=$(gift_suggestions "$person") message+="\n\n$gifts" should_remind=true fi elif ((days_until == days_before)); then reminder_type="week" if ! reminder_sent "$name" "$reminder_type"; then message="šŸ“… *Birthday Reminder*\n\n*$name* — your $relationship$age_text\nBirthday in *$days_until days* ($birthday)\n\nTime to plan something! šŸŽ" local gifts=$(gift_suggestions "$person") message+="\n\n$gifts" should_remind=true fi elif ((days_until == weeks_before * 7)); then reminder_type="twoweeks" if ! reminder_sent "$name" "$reminder_type"; then message="šŸ“… *Upcoming Birthday*\n\n*$name* — your $relationship$age_text\nBirthday in *$days_until days* ($birthday)\n\nStart thinking about gifts! šŸŽ" local gifts=$(gift_suggestions "$person") message+="\n\n$gifts" should_remind=true fi fi if [[ "$should_remind" == true ]]; then log "Sending $reminder_type reminder for $name" send_telegram "$message" log_reminder "$name" "$reminder_type" ((reminders_sent++)) fi done <<< "$people" log "Sent $reminders_sent reminders" } # Add/update a person (can be called manually) add_person() { local name="$1" local relationship="$2" local birthday="$3" # MM-DD format local notes="$4" local temp_file=$(mktemp) jq --arg name "$name" \ --arg rel "$relationship" \ --arg bday "$birthday" \ --arg notes "$notes" \ '.people += [{"name": $name, "relationship": $rel, "birthday": $bday, "notes": $notes, "gift_ideas": [], "past_gifts": []}]' \ "$DATA_FILE" > "$temp_file" mv "$temp_file" "$DATA_FILE" log "Added $name to birthday tracker" } # Log a past gift log_gift() { local name="$1" local gift="$2" local year="${3:-$(date +%Y)}" local temp_file=$(mktemp) jq --arg name "$name" \ --arg gift "$gift ($year)" \ '(.people[] | select(.name == $name)).past_gifts += [$gift]' \ "$DATA_FILE" > "$temp_file" mv "$temp_file" "$DATA_FILE" log "Logged gift for $name: $gift" } # Main main() { case "${1:-check}" in check) init_data check_birthdays cleanup_log ;; add) add_person "$2" "$3" "$4" "$5" ;; gift) log_gift "$2" "$3" "$4" ;; list) jq '.people[] | {name, relationship, birthday, notes}' "$DATA_FILE" ;; *) echo "Usage: $0 [check|add|gift|list]" echo " check - Run daily birthday check" echo " add 'Name' 'Relationship' 'MM-DD' 'Notes' - Add new person" echo " gift 'Name' 'Gift description' [year] - Log a gift given" echo " list - Show all tracked birthdays" ;; esac } main "$@"