Skip to content

Instantly share code, notes, and snippets.

@bulletinmybeard
Last active January 7, 2024 11:32
Show Gist options
  • Save bulletinmybeard/13f86f5515e54dc1f9a173c8be1d4bd7 to your computer and use it in GitHub Desktop.
Save bulletinmybeard/13f86f5515e54dc1f9a173c8be1d4bd7 to your computer and use it in GitHub Desktop.
Download and overwrite the updated remote gists via this bash script

The bash script below will download all users gists from https://api.github.com/users/<github-username>/gists and need the GitHub username and GitHub API token as the rate limit for unauthenticated requests is 👎

# Unauthenticated
{
	"rate": {
		"limit": 60,
		"remaining": 59,
		"reset": 1704592325,
		"used": 1,
		"resource": "core"
	}
}

# Authenticated
{
	"rate": {
		"limit": 5000,
		"used": 0,
		"remaining": 5000,
		"reset": 1704592470
	}
}
  • Create a bash script using the code below
  • Make the script executable by using the command chmod +x export_gists.sh
  • Create a GitHub API token in your GitHub settings and only enable the gists permission
  • Run the script with ./export_gists.sh (E.g., ./export_gists.sh bulletinmybeard *******)
#!/bin/bash

# Define the timezone offset from UTC (in hours)
timezone_offset=1

# Check if a username and bearer token are provided as arguments
if [ -z "$1" ] || [ -z "$2" ]; then
    printf "Error: Missing arguments!\nUsage: %s <github-username> <bearer-token>" "$0"
    exit 1
fi

# Assign the provided username and bearer token to variables
username="$1"
bearer_token="$2"

# Create a directory based on the username if it doesn't exist
export_dir="export_${username}"
mkdir -p "$export_dir"

# cURL helper function
run_curl_cmd() {
    local url=$1
    curl -sS \
        -H "Accept: application/vnd.github+json" \
        -H "Authorization: Bearer ${bearer_token}" \
        -H "X-GitHub-Api-Version: 2022-11-28" \
        "$url"
}

# Fetch and process the rate limit info
fetch_rate_limit() {
   rate_limit_url="https://api.github.com/rate_limit"
   response=$(run_curl_cmd "$rate_limit_url")
   echo "$response" | jq '.rate'
}

convert_timestamp_to_date() {
    local timestamp=$1
    # Use date command to convert the UNIX timestamp
    # '%F %T' will output the date in 'YYYY-MM-DD HH:MM:SS' format
    date -r "$timestamp" '+%F %T'
}

calculate_time_until_reset() {
    local reset_timestamp=$1

    # Get the current time in Unix timestamp
    current_timestamp=$(date +%s)

    # Calculate the difference in seconds
    diff_seconds=$((reset_timestamp - current_timestamp))

    # Check if the difference is negative (reset time has passed)
    if [ $diff_seconds -lt 0 ]; then
        echo "The reset time has already passed."
        return
    fi

    # Convert the difference to hours, minutes, and seconds
    hours=$(printf "%02d" $((diff_seconds / 3600)))
    minutes=$(printf "%02d" $(( (diff_seconds % 3600) / 60 )))
    seconds=$(printf "%02d" $(( diff_seconds % 60 )))

    echo "${hours}:${minutes}:${seconds}"
}

rate_limit_info=$(fetch_rate_limit)
request_limit=$(echo "$rate_limit_info" | jq '.limit')
remaining_requests=$(echo "$rate_limit_info" | jq '.remaining')
limit_reset=$(echo "$rate_limit_info" | jq '.reset')
limit_reset_datetime=$(convert_timestamp_to_date "$limit_reset")
time_until_reset=$(calculate_time_until_reset "$limit_reset")

# Check the current usage
if [ "$remaining_requests" -ne 0 ]; then
    printf "\nYou have %s/%s remaining API requests\n=================================================\n" "$remaining_requests" "$request_limit"
else
    echo "You hit the API request limit ($request_limit). Limit will reset in: $time_until_reset ($limit_reset_datetime)"
    exit 0
fi

# Adjust adjust the local timestamp for CET to UTC
adjust_for_cet_to_utc() {
    local timestamp=$1
    local offset_seconds=$((timezone_offset * 3600))
    echo "$timestamp - $offset_seconds"
}

# Fetch and process paginated gists
fetch_paginated_gists() {
    local page=$1
    local url="https://api.github.com/users/${username}/gists?page=${page}"

    # Fetch the gists for the current page using the Bearer token
    gists=$(run_curl_cmd "$url")

    # Count the number of gists in the response
    gist_count=$(echo "$gists" | jq '. | length')

    # Check if the response is empty
    if [ "$gist_count" -eq 0 ]; then
      echo "end"
    fi

    # Process the gists
    echo "$gists" | jq -r '.[] | {url: .files | to_entries[] | .value.raw_url, updated_at} | @base64' | \
    while IFS= read -r line; do
        # Decode the JSON string
        json=$(echo "$line" | base64 -D)

        # Extract the URL and updated_at value
        url=$(echo "$json" | jq -r '.url')
        updated_at=$(echo "$json" | jq -r '.updated_at')

        # Convert updated_at to seconds since Unix epoch (UTC)
        updated_at=$(date -jf "%Y-%m-%dT%H:%M:%SZ" "$updated_at" +%s)

        # Extract filename and create a path within the export directory
        filename=$(basename "$url")
        file_path="${export_dir}/${filename}"

        # Check if the file exists
        if [ -f "$file_path" ]; then
            # Get the last modification time of the local file in seconds since Unix epoch (local time)
            local_updated_at=$(date -r "$file_path" +%s)
            # Adjust local file timestamp from CET to UTC
            local_updated_at=$(adjust_for_cet_to_utc $local_updated_at)

            local_updated_at_int=$((local_updated_at))
            updated_at_int=$((updated_at))

            # Download the file if the local copy is older
            if [ "$local_updated_at_int" -lt "$updated_at_int" ]; then
                echo "Updating $file_path as it is older than the remote version."
                curl -sS -o "$file_path" "$url"
            else
                echo "$file_path is up to date."
            fi
        else
            echo "Downloading new file: $file_path"
            curl -sS -o "$file_path" "$url"
        fi
    done
}

# Main loop to iterate over gist pages
page=1
while true; do
    page_result=$(fetch_paginated_gists "$page")

    if [[ -n "$page_result" ]] && [[ "$page_result" != *"end"* ]]; then
        printf "\nProcessing Page #%s\n-------------------\n" "$page"
        echo "$page_result"
        ((page++))
    else
        printf "\nAll gists have been processed!"
        break
    fi
done
./export_gists.sh bulletinmybeard **********

You have 4719/5000 remaining API requests
=================================================

Processing Page #1
-------------------
export_bulletinmybeard/list-network-connections-and-transform-them-into-json.md is up to date.
export_bulletinmybeard/chat.openai.com-userscript.js is up to date.
export_bulletinmybeard/fix-cannot-connect-to-the-docker-daemon-error-on-macoslinux.md is up to date.
export_bulletinmybeard/bol.com-userscript.js is up to date.
export_bulletinmybeard/debug-python-apps-via-docker-composeyml-in-pycharm.md is up to date.
export_bulletinmybeard/local-nginx-server-site-with-valid-ssl-certificate.md is up to date.
export_bulletinmybeard/python-script-to-alter-file-creation-date-from-filename.md is up to date.
export_bulletinmybeard/macos-prevent-flux-from-automatically-toggling-light-and-dark-mode.md is up to date.
export_bulletinmybeard/change-default-login-shell-on-macos-ventura.md is up to date.
export_bulletinmybeard/customize-default-syntax-highlighter-in-atom-editor-macos--linux.md is up to date.
export_bulletinmybeard/list-installed-homebrew-packages-in-tree-view-macos.md is up to date.
export_bulletinmybeard/delete-all-node_modules-folders-recursively.md is up to date.
export_bulletinmybeard/resolve-tabnine-subscription-sync-issues-in-your-ide.md is up to date.
export_bulletinmybeard/using-prompt-eol-mark-to-remove-extra-trailing-mark-in-terminal-output.md is up to date.
export_bulletinmybeard/capture-audio-file-loudness-levels-with-ffmpeg-perl-regex-and-jq.md is up to date.
export_bulletinmybeard/change-bookmark-icons-in-chrome-desktop-with-the-favicon-changer-in-2023.md is up to date.
export_bulletinmybeard/sitemap-xml-location-for-custom-medium-blogs.md is up to date.
export_bulletinmybeard/macos-toggle-google-chrome-swipe-navigation.md is up to date.
export_bulletinmybeard/put-io-move-to-folder-modal-input-search-filter.md is up to date.
export_bulletinmybeard/accessing-private-aws-cloud-resources-from-your-local-machine.md is up to date.
export_bulletinmybeard/git-log-json-output.md is up to date.
export_bulletinmybeard/prowritingaid-com-auto-expand-menu-from-the-go.md is up to date.
export_bulletinmybeard/reset-macos-notifications.md is up to date.
export_bulletinmybeard/tinder-auto-like-userscript.md is up to date.
export_bulletinmybeard/macos-kill-audio-command.md is up to date.
export_bulletinmybeard/fix-virtualbox-docker-machine-host-issues.md is up to date.
export_bulletinmybeard/run-screensaver-delayed-in-termnial.md is up to date.
export_bulletinmybeard/use-redis-in-lua-script-nginx.md is up to date.
export_bulletinmybeard/fix-for-sudo-command-error-unable-to-initialize-pam-no-such-file-or-directory.md is up to date.
export_bulletinmybeard/enforce-macos-icloud-sync.md is up to date.

Processing Page #2
-------------------
export_bulletinmybeard/patching-macos-apps-with-working-and-valid-app-signature.md is up to date.
export_bulletinmybeard/list-of-user-agents.md is up to date.
export_bulletinmybeard/quick-guide-publish-npm-packages.md is up to date.
export_bulletinmybeard/content-scraping-with-browser-devtools.js.md is up to date.
export_bulletinmybeard/list-first-level-package-details.js.md is up to date.
export_bulletinmybeard/automatic-content-updates-in-a-running-ios-simulator-instance.md is up to date.
export_bulletinmybeard/disable-the-git-integration-in-visual-studio-code.md is up to date.
export_bulletinmybeard/sublime-text-3-user-settings.md is up to date.
export_bulletinmybeard/visual-studio-code-user-settings-json.md is up to date.

All gists have been processed!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment