#!/bin/bash
## ------------------------------------
##  Made By: Michael Yochpaz (C) 2020
##  https://github.com/MichaelYochpaz/Backup-Script
##  Version: 1.2.0
##  License: GPLv3
## ------------------------------------
## -------------- Usage ---------------
##  The script generates a tar.gz backup file of a folder, and according to user's given arguments,
##  can upload it to a cloud service using rclone and remove the local copy.
##
## ------------ Requirments -----------
##  - In order to use the upload feature, rclone must be installed and have at least one cloud service setup in config.
## ------------------------------------
##
##  Usage: backup [-n <name>] [-s <path>] [-e <pattern>]... [-u <path>] [-p <API key>] [-r] [-y] [-v] <path-to-backup>
##
##  Options:
##    -n <name>     Sets the tar.gz file name [default: "backup"]
##    -s <path>     Path to which the generated backup file will be saved to [default: current working directory]
##    -e <pattern>  Exclude a pattern (specific files / folders) from being backed up
##    -u <path>     rclone path to which the backup file will be uploaded to (not providing one will skip the upload process)
##    -p <API key>  Sends a Pushbullet notification once backup is done
##    -r            Removes local copy of backup file after it's been uploaded
##    -y            Skip warnings (Warnings require user input to continue by default)
##    -v            Uses '-v' option when running tar and rclone
##
##  Commands:
##    -h            Displays this help message and exists.
##
##  Examples:
##    backup "/home/user/important_stuff"
##    backup -u "GDrive:/Backups" -r -y -p "XXXXXXXXXXXXXXXX" "/home/user/important_stuff/" 
##    backup -n "important-stuff-backup" -s "/home/user/backups" -e "*.pdf" -e "important_stuff/dont_backup_this_folder" "/home/user/important_stuff/"
##
## --------- Configuration ------------
BACKUP_NAME="backup" # Backup file name. date will be added to file name after this string.
BACKUP_FOLDER="" # Folder to backup (full path).
EXCLUDES=() # Exclusion patterns array. Usage example: ("*.mkv" "*.pdf" "important_stuff/dont_backup_this_folder")
SAVE_FOLDER="$PWD" # Folder to save backup file to (full path)
RCLONE_FOLDER="" # rclone folder path to upload backup file to. Leave empty to skip the upload process.
PUSHBULLET_API_KEY="" # Pusbullet API key for notifications.
REMOVE_LOCAL=false # Boolean - Remove local backup file after upload.
SKIP_WARNINGS=false # Boolean - Skip warnings.
VERBOSE=false # Boolean - Use verbose mode when running tar and rclone.
PUSHBULLET_TITLE="Backup Script" # Pusbullet - Notification title.
PUSHBULLET_MESSAGE_START="Backup script " # Pushbullet - Notification messages will start with this string, followed by one of the followings:
PUSHBULLET_MESSAGE_FINISHED_SUCCESS="finished successfully." # Pusbullet - Message will be shown if the script ran successfully.
PUSHBULLET_MESSAGE_FINISHED_ERRORS="finished with errors." # Pusbullet - Message will be shown if the script finished with errors.
PUSHBULLET_MESSAGE_FAIL="failed." # Pusbullet - Message will be shown if the script has failed.
## ------------------------------------

usage() { echo "Usage: $(basename $0) [-n <name>] [-s <path>] [-e <pattern>]... [-u <path>] [-p <API key>] [-r] [-y] [-v] <path-to-backup>
Use $(basename $0) -h for additional info."; exit 1; }

# if there is a Pushbullet API key, send a Pushbullet notification. uses a parameter for message's body
pushbullet() { if [ -n "$PUSHBULLET_API_KEY" ]; then curl -u "$PUSHBULLET_API_KEY": https://api.pushbullet.com/v2/pushes -d type=note -d \
 title="$PUSHBULLET_TITLE" -d body="$PUSHBULLET_MESSAGE_START$1 " >/dev/null 2>&1; fi }

ERROR=false # sets to true if there's an error. used for final exit code of the script.

C0='\033[0m' # ANSI - no color
C1='\033[0;32m' # ANSI - success - green
C2='\033[1;31m' # ANSI - error - red
C3='\033[0;36m' # ANSI - information - cyan
C4='\033[0;33m' # ANSI - variables/paths - orange

while getopts ":n:e:s:u:p:ryvh" arg; do
  case $arg in
    n)
      BACKUP_NAME=${OPTARG}
      ;;
    e)
      EXCLUDES+=("${OPTARG}")
      ;;
    s)
      SAVE_FOLDER=${OPTARG}
      ;;
    u)
      RCLONE_FOLDER=${OPTARG}
      ;;
    p)
      PUSHBULLET_API_KEY=${OPTARG}
      ;;
    r)
      REMOVE_LOCAL=true
      ;;
    y)
      SKIP_WARNINGS=true
      ;;
    v)
      VERBOSE=true
      ;;
    h)
      echo " Usage: $(basename $0) [-n <name>] [-s <path>] [-e <pattern>]... [-u <path>] [-p <API key>] [-r] [-y] [-v] <path-to-backup>
 Options:
   -n <name>     Sets the tar.gz file name [default: "backup"]
   -s <path>     Path to which the generated backup file will be saved to [default: current working directory]
   -e <pattern>  Exclude a pattern (specific files / folders) from being backed up
   -u <path>     rclone path to which the backup file will be uploaded to (not providing one will skip the upload process)
   -p <API key>  Sends a Pushbullet notification once backup is done
   -r            Removes local copy of backup file after it's been uploaded
   -y            Skip warnings (Warnings require user input to continue by default)
   -v            Uses '-v' option when running tar and rclone

 Commands:
   -h            Displays this help message and exists.

 Examples:
   $(basename $0) \"/home/user/important_stuff\"
   $(basename $0) -u \"GDrive:/Backups\" -r -y -p \"XXXXXXXXXXXXXXXX\" \"/home/user/important_stuff/\" 
   $(basename $0) -n \"important-stuff-backup\" -s \"/home/user/backups\" -e \"*.pdf\" -e \"important_stuff/dont_backup_this_folder\" \"/home/user/important_stuff/\""
      exit 0
      ;;
    *)
      usage
      ;;
  esac
done

# positional argument - the folder that's going to be backed-up
if [[ -n "${@:$OPTIND:1}" ]]; then BACKUP_FOLDER=${@:$OPTIND:1}; fi
# if no backup folder was chosen, show usage
if [ -z "$BACKUP_FOLDER" ]; then usage; fi 
# if BACKUP_FOLDER path doesn't exist, show error and exit
if [ ! -d $BACKUP_FOLDER ]; then 
  echo -e "${C2}Folder ${C4}$BACKUP_FOLDER ${C2}not found${C0}"
  pushbullet "$PUSHBULLET_MESSAGE_FAIL"
  exit 1
fi
# if SAVE_FOLDER path doesn't exist, show error and exit
if [ ! -d $SAVE_FOLDER ]; then 
  echo -e "${C2}Folder ${C4}$SAVE_FOLDER ${C2}not found${C0}"
  pushbullet "$PUSHBULLET_MESSAGE_FAIL"
  exit 1
fi
# if BACKUP_FOLDER path ends with a '/', remove it
if [ "${BACKUP_FOLDER: -1}" = "/" ]; then BACKUP_FOLDER=${BACKUP_FOLDER%?}; fi 
# if SAVE_FOLDER path ends with a '/', remove it
if [ "${SAVE_FOLDER: -1}" = "/" ]; then SAVE_FOLDER=${SAVE_FOLDER%?}; fi
 # if REMOVE_LOCAL is set to true, but rclone upload is not used, show a warning.
if [ "$REMOVE_LOCAL" = true ] && [ -z "$RCLONE_FOLDER" ] && [] "$SKIP_WARNINGS" = false ]; then
  read -p "WARNING: '-r' argument to remove local backup file was used,
  but no rclone path to upload backup flie to beforehand is set.
  This will result in generating a backup file and deleting it after without it being uploaded anywhere.
  Would you like to continue (Y/n) ?" -n 1 -r
  echo

  if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 0; fi
fi

# ----- tar -----
echo -e "$(date +"%Y/%m/%d %T") ${C3}Generating tar.gz file"${C0}
FILENAME="${BACKUP_NAME}-$(date +%FT%H%M).tar.gz"
# generate a temporary file to store EXCLUDES in
EXCLUEDS_FILE=$(mktemp /tmp/backup.XXXXXX.txt) 
if [ ! ${#EXCLUDES[@]} -eq 0 ]; then printf "%s\n" "${EXCLUDES[@]}" > "$EXCLUEDS_FILE"; fi

# run tar with or without '-v', according to VERBOSE value
if [ "$VERBOSE" = true ]; then tar -cvzf "$SAVE_FOLDER/$FILENAME" -C "$BACKUP_FOLDER/.." -X $EXCLUEDS_FILE "$(basename "$BACKUP_FOLDER")"; \
else tar -czf "$SAVE_FOLDER/$FILENAME" -C "$BACKUP_FOLDER/.." -X $EXCLUEDS_FILE "$(basename "$BACKUP_FOLDER")"; fi

# remove temporary file
rm $EXCLUEDS_FILE

# tar commmand finished successfully (exit code = 0)
if [ "$?" = 0 ]; then
  echo -e "$(date +"%Y/%m/%d %T") ${C1}Backup file generated and saved successfully to ${C4}$SAVE_FOLDER/$FILENAME ($(du -h "$SAVE_FOLDER/$FILENAME" | cut -f1))${C0}"
# tar commmand didn't run successfully (exit code != 0)
else 
  echo -e "$(date +"%Y/%m/%d %T") ${C2}Backup file generation failed${C0}"
# if a file was generated during the (failed) tar process, remove it
  if [ -f "$SAVE_FOLDER/$FILENAME" ]; then rm "$SAVE_FOLDER/$FILENAME"; fi
  pushbullet "$PUSHBULLET_MESSAGE_FAIL"
  exit 1
fi

# ----- rclone -----
if [ -n "$RCLONE_FOLDER" ]; then
  echo -e "$(date +"%Y/%m/%d %T") ${C3}Uploading backup file to ${C4}$RCLONE_FOLDER${C0}"
  # run rclone with or without '-v', according to VERBOSE value
  if [ "$VERBOSE" = true ]; then rclone copy "$SAVE_FOLDER/$FILENAME" -v "$RCLONE_FOLDER"; else rclone copy "$SAVE_FOLDER/$FILENAME" "$RCLONE_FOLDER"; fi
  # rclone commmand finished successfully (exit code = 0)
  if [ "$?" = 0 ]; then
    echo -e "$(date +"%Y/%m/%d %T") ${C1}Backup file uploaded successfully${C0}"
  # rclone command didn't run successfully (exit code != 0)
  else
    ERROR=true
    echo -e "$(date +"%Y/%m/%d %T") ${C2}Upload failed${C0}"

    if [ "$REMOVE_LOCAL" = true ] && [ "$SKIP_WARNINGS" = false ]; then
      read -p "WARNING: Would you like to continute and remove local copy of the backup file even though upload wasn't successful (Y/n) ? " -n 1 -r
      echo
      if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        echo -e "$(date +"%Y/%m/%d %T") ${C2}Script finished with errors${C0}"
        exit 1
      fi
    fi
  fi
fi
# remove local backup if REMOVE_LOCAL setting used
if [ "$REMOVE_LOCAL" = true ]; then
  rm "$SAVE_FOLDER/$FILENAME"
  echo -e "$(date +"%Y/%m/%d %T") ${C1}Local file removed${C0}"
fi

# print, send Pushbullet notification (if used), and exit using correct code according to ERROR's value
if [ "$ERROR" = false ]; then
  echo -e "$(date +"%Y/%m/%d %T") ${C1}Script finished successfully${C0}"
  pushbullet "$PUSHBULLET_MESSAGE_FINISHED_SUCCESS"
  exit 0;

else
  echo -e "$(date +"%Y/%m/%d %T") ${C2}Script finished with errors${C0}"
  pushbullet "$PUSHBULLET_MESSAGE_FINISHED_ERRORS"
  exit 1
fi