- FreshRSS Smart Digest: Daily AI-ranked RSS summary at 7 AM - Birthday Tracker: Smart reminders for family birthdays with gift suggestions - Home Stack Monitor: Health checks every 15 min with self-healing attempts All cron jobs configured and ready to run. Telegram bot token saved to .env
323 lines
9.8 KiB
Bash
Executable File
323 lines
9.8 KiB
Bash
Executable File
#!/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 "$@"
|