From ad5e1de8a716871d93c22880bd977fca275be6dd Mon Sep 17 00:00:00 2001
From: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7
 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
Date: Thu, 25 Jun 2026 15:11:09 -0400
Subject: [PATCH 1/3] =?UTF-8?q?chore:=20add=20grab-emoji.sh=20Slack?=
 =?UTF-8?q?=E2=86=92Buzz=20emoji=20import=20script?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Utility script that fetches custom Slack emoji by name and uploads
them to Buzz via `buzz upload file`. Useful for migrating workspace
emoji to Buzz without manual download/upload steps.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
---
 scripts/grab-emoji.sh | 165 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 165 insertions(+)
 create mode 100755 scripts/grab-emoji.sh

diff --git a/scripts/grab-emoji.sh b/scripts/grab-emoji.sh
new file mode 100755
index 0000000000..549fb39037
--- /dev/null
+++ b/scripts/grab-emoji.sh
@@ -0,0 +1,165 @@
+#!/usr/bin/env bash
+# grab-emoji.sh — Download custom Slack emoji and upload to Buzz
+#
+# Usage:
+#   SLACK_TOKEN=xoxp-... ./scripts/grab-emoji.sh shipitparrot partyparrot tada
+#
+# Env:
+#   SLACK_TOKEN  — Slack user token (xoxp-...) with emoji:read scope
+#
+# Output:
+#   name → buzz_url      on success
+#   name → ERROR: reason on failure (script continues to next emoji)
+
+set -euo pipefail
+
+CACHE_FILE="${HOME}/.cache/slack-emoji-list.json"
+CACHE_TTL=86400  # 24 hours in seconds
+
+# ── Preflight checks ──────────────────────────────────────────────────────────
+
+if [[ $# -eq 0 ]]; then
+  echo "Usage: SLACK_TOKEN=xoxp-... $0 <emoji-name> [emoji-name ...]" >&2
+  exit 1
+fi
+
+if [[ -z "${SLACK_TOKEN:-}" ]]; then
+  echo "ERROR: SLACK_TOKEN is not set. Export your xoxp- Slack token." >&2
+  exit 1
+fi
+
+if ! command -v buzz &>/dev/null; then
+  echo "ERROR: 'buzz' not found in PATH. Install the Buzz CLI and retry." >&2
+  exit 1
+fi
+
+if ! command -v curl &>/dev/null; then
+  echo "ERROR: 'curl' not found in PATH." >&2
+  exit 1
+fi
+
+if ! command -v jq &>/dev/null; then
+  echo "ERROR: 'jq' not found in PATH." >&2
+  exit 1
+fi
+
+# ── Cache management ──────────────────────────────────────────────────────────
+
+_cache_is_fresh() {
+  [[ -f "$CACHE_FILE" ]] || return 1
+  local mtime now age
+  # macOS stat uses -f %m; GNU stat uses -c %Y
+  if stat --version &>/dev/null 2>&1; then
+    mtime=$(stat -c %Y "$CACHE_FILE")
+  else
+    mtime=$(stat -f %m "$CACHE_FILE")
+  fi
+  now=$(date +%s)
+  age=$(( now - mtime ))
+  (( age < CACHE_TTL ))
+}
+
+_refresh_cache() {
+  mkdir -p "$(dirname "$CACHE_FILE")"
+  local response
+  response=$(curl -sf \
+    -H "Authorization: Bearer ${SLACK_TOKEN}" \
+    "https://slack.com/api/emoji.list") || {
+    echo "ERROR: network failure fetching emoji list from Slack" >&2
+    return 1
+  }
+
+  local ok
+  ok=$(echo "$response" | jq -r '.ok')
+  if [[ "$ok" != "true" ]]; then
+    local err
+    err=$(echo "$response" | jq -r '.error // "unknown error"')
+    echo "ERROR: Slack API returned error: $err" >&2
+    return 1
+  fi
+
+  echo "$response" > "$CACHE_FILE"
+}
+
+if ! _cache_is_fresh; then
+  _refresh_cache || exit 1
+fi
+
+# ── Emoji resolution ──────────────────────────────────────────────────────────
+
+# Returns the URL for an emoji name, resolving one level of aliasing.
+# Prints the URL to stdout; returns 1 if not found.
+_resolve_url() {
+  local name="$1"
+  local value
+  value=$(jq -r --arg n "$name" '.emoji[$n] // empty' "$CACHE_FILE")
+
+  if [[ -z "$value" ]]; then
+    return 1
+  fi
+
+  if [[ "$value" == alias:* ]]; then
+    local target="${value#alias:}"
+    value=$(jq -r --arg n "$target" '.emoji[$n] // empty' "$CACHE_FILE")
+    if [[ -z "$value" ]]; then
+      return 1
+    fi
+  fi
+
+  echo "$value"
+}
+
+# ── Per-emoji processing ──────────────────────────────────────────────────────
+
+for emoji_name in "$@"; do
+  # Resolve URL
+  emoji_url=$(_resolve_url "$emoji_name") || {
+    echo "${emoji_name} → ERROR: emoji not found in workspace"
+    continue
+  }
+
+  # Determine extension from URL (default to .png if none found)
+  ext="${emoji_url##*.}"
+  # Strip any query string from extension
+  ext="${ext%%\?*}"
+  case "$ext" in
+    png|gif|jpg|jpeg|webp) ;;
+    *) ext="png" ;;
+  esac
+
+  # Download to temp file
+  tmp_file=$(mktemp "/tmp/slack-emoji-XXXXXX.${ext}")
+  trap 'rm -f "$tmp_file"' EXIT
+
+  if ! curl -sf -o "$tmp_file" "$emoji_url"; then
+    echo "${emoji_name} → ERROR: failed to download emoji from ${emoji_url}"
+    rm -f "$tmp_file"
+    trap - EXIT
+    continue
+  fi
+
+  # Upload to Buzz
+  upload_output=$(buzz upload file "$tmp_file" 2>&1) || {
+    echo "${emoji_name} → ERROR: buzz upload failed — ${upload_output}"
+    rm -f "$tmp_file"
+    trap - EXIT
+    continue
+  }
+
+  # Extract URL from BlobDescriptor output
+  # buzz upload file prints a multi-line BlobDescriptor; the url field is first
+  buzz_url=$(echo "$upload_output" | grep -E '^url:' | awk '{print $2}' | head -1)
+  if [[ -z "$buzz_url" ]]; then
+    # Fallback: try JSON parse if output is JSON
+    buzz_url=$(echo "$upload_output" | jq -r '.url // empty' 2>/dev/null || true)
+  fi
+
+  if [[ -z "$buzz_url" ]]; then
+    echo "${emoji_name} → ERROR: could not parse Buzz URL from upload output"
+  else
+    echo "${emoji_name} → ${buzz_url}"
+  fi
+
+  rm -f "$tmp_file"
+  trap - EXIT
+done

From 601f15ba0b68682311f702824ce86a37fbde77bc Mon Sep 17 00:00:00 2001
From: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7
 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
Date: Thu, 25 Jun 2026 15:35:38 -0400
Subject: [PATCH 2/3] fix: use buzz emoji set instead of buzz upload file
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The script was downloading the emoji file and uploading it as a raw
blob. The correct behavior is to register the emoji in Buzz's custom
emoji set so it appears in the emoji picker as :name:. Switch to
`buzz emoji set --shortcode <name> --url <slack-cdn-url>`, which
takes the CDN URL directly — no download or temp file needed.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
---
 scripts/grab-emoji.sh | 54 ++++++++-----------------------------------
 1 file changed, 10 insertions(+), 44 deletions(-)

diff --git a/scripts/grab-emoji.sh b/scripts/grab-emoji.sh
index 549fb39037..2ba15dd688 100755
--- a/scripts/grab-emoji.sh
+++ b/scripts/grab-emoji.sh
@@ -1,5 +1,8 @@
 #!/usr/bin/env bash
-# grab-emoji.sh — Download custom Slack emoji and upload to Buzz
+# grab-emoji.sh — Register custom Slack emoji in Buzz
+#
+# Looks up each emoji name in your Slack workspace and registers it in Buzz
+# via `buzz emoji set`, making it available as :name: in the Buzz emoji picker.
 #
 # Usage:
 #   SLACK_TOKEN=xoxp-... ./scripts/grab-emoji.sh shipitparrot partyparrot tada
@@ -8,8 +11,8 @@
 #   SLACK_TOKEN  — Slack user token (xoxp-...) with emoji:read scope
 #
 # Output:
-#   name → buzz_url      on success
-#   name → ERROR: reason on failure (script continues to next emoji)
+#   name → registered as :name: in Buzz   on success
+#   name → ERROR: reason                  on failure (script continues to next emoji)
 
 set -euo pipefail
 
@@ -118,48 +121,11 @@ for emoji_name in "$@"; do
     continue
   }
 
-  # Determine extension from URL (default to .png if none found)
-  ext="${emoji_url##*.}"
-  # Strip any query string from extension
-  ext="${ext%%\?*}"
-  case "$ext" in
-    png|gif|jpg|jpeg|webp) ;;
-    *) ext="png" ;;
-  esac
-
-  # Download to temp file
-  tmp_file=$(mktemp "/tmp/slack-emoji-XXXXXX.${ext}")
-  trap 'rm -f "$tmp_file"' EXIT
-
-  if ! curl -sf -o "$tmp_file" "$emoji_url"; then
-    echo "${emoji_name} → ERROR: failed to download emoji from ${emoji_url}"
-    rm -f "$tmp_file"
-    trap - EXIT
-    continue
-  fi
-
-  # Upload to Buzz
-  upload_output=$(buzz upload file "$tmp_file" 2>&1) || {
-    echo "${emoji_name} → ERROR: buzz upload failed — ${upload_output}"
-    rm -f "$tmp_file"
-    trap - EXIT
+  # Register in Buzz
+  set_output=$(buzz emoji set --shortcode "$emoji_name" --url "$emoji_url" 2>&1) || {
+    echo "${emoji_name} → ERROR: buzz emoji set failed — ${set_output}"
     continue
   }
 
-  # Extract URL from BlobDescriptor output
-  # buzz upload file prints a multi-line BlobDescriptor; the url field is first
-  buzz_url=$(echo "$upload_output" | grep -E '^url:' | awk '{print $2}' | head -1)
-  if [[ -z "$buzz_url" ]]; then
-    # Fallback: try JSON parse if output is JSON
-    buzz_url=$(echo "$upload_output" | jq -r '.url // empty' 2>/dev/null || true)
-  fi
-
-  if [[ -z "$buzz_url" ]]; then
-    echo "${emoji_name} → ERROR: could not parse Buzz URL from upload output"
-  else
-    echo "${emoji_name} → ${buzz_url}"
-  fi
-
-  rm -f "$tmp_file"
-  trap - EXIT
+  echo "${emoji_name} → registered as :${emoji_name}: in Buzz"
 done

From 76a4fcf5c5f7720cca690dbb4782dc00aa36db83 Mon Sep 17 00:00:00 2001
From: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7
 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
Date: Thu, 25 Jun 2026 15:45:48 -0400
Subject: [PATCH 3/3] feat(scripts): add --name flag to grab-emoji.sh to
 override Buzz shortcode

Allows registering a Slack emoji under a different name in Buzz:

  ./scripts/grab-emoji.sh --name teamwork teamwork2

Fetches the Slack emoji 'teamwork2' and registers it as :teamwork: in Buzz.
--name is validated to only work with a single emoji arg.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
---
 scripts/grab-emoji.sh | 43 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 39 insertions(+), 4 deletions(-)

diff --git a/scripts/grab-emoji.sh b/scripts/grab-emoji.sh
index 2ba15dd688..9c959941e1 100755
--- a/scripts/grab-emoji.sh
+++ b/scripts/grab-emoji.sh
@@ -5,7 +5,10 @@
 # via `buzz emoji set`, making it available as :name: in the Buzz emoji picker.
 #
 # Usage:
-#   SLACK_TOKEN=xoxp-... ./scripts/grab-emoji.sh shipitparrot partyparrot tada
+#   SLACK_TOKEN=xoxp-... ./scripts/grab-emoji.sh [--name <buzz-name>] <emoji-name> [emoji-name ...]
+#
+# Options:
+#   --name <buzz-name>  Override the shortcode used in Buzz (only valid with a single emoji)
 #
 # Env:
 #   SLACK_TOKEN  — Slack user token (xoxp-...) with emoji:read scope
@@ -19,10 +22,39 @@ set -euo pipefail
 CACHE_FILE="${HOME}/.cache/slack-emoji-list.json"
 CACHE_TTL=86400  # 24 hours in seconds
 
+# ── Argument parsing ──────────────────────────────────────────────────────────
+
+BUZZ_NAME=""
+
+while [[ $# -gt 0 ]]; do
+  case "$1" in
+    --name)
+      BUZZ_NAME="$2"
+      shift 2
+      ;;
+    --)
+      shift
+      break
+      ;;
+    -*)
+      echo "ERROR: Unknown option: $1" >&2
+      exit 1
+      ;;
+    *)
+      break
+      ;;
+  esac
+done
+
 # ── Preflight checks ──────────────────────────────────────────────────────────
 
 if [[ $# -eq 0 ]]; then
-  echo "Usage: SLACK_TOKEN=xoxp-... $0 <emoji-name> [emoji-name ...]" >&2
+  echo "Usage: SLACK_TOKEN=xoxp-... $0 [--name <buzz-name>] <emoji-name> [emoji-name ...]" >&2
+  exit 1
+fi
+
+if [[ -n "$BUZZ_NAME" && $# -ne 1 ]]; then
+  echo "ERROR: --name can only be used when specifying a single emoji" >&2
   exit 1
 fi
 
@@ -115,6 +147,9 @@ _resolve_url() {
 # ── Per-emoji processing ──────────────────────────────────────────────────────
 
 for emoji_name in "$@"; do
+  # Use --name override if provided, otherwise use the Slack emoji name
+  buzz_shortcode="${BUZZ_NAME:-$emoji_name}"
+
   # Resolve URL
   emoji_url=$(_resolve_url "$emoji_name") || {
     echo "${emoji_name} → ERROR: emoji not found in workspace"
@@ -122,10 +157,10 @@ for emoji_name in "$@"; do
   }
 
   # Register in Buzz
-  set_output=$(buzz emoji set --shortcode "$emoji_name" --url "$emoji_url" 2>&1) || {
+  set_output=$(buzz emoji set --shortcode "$buzz_shortcode" --url "$emoji_url" 2>&1) || {
     echo "${emoji_name} → ERROR: buzz emoji set failed — ${set_output}"
     continue
   }
 
-  echo "${emoji_name} → registered as :${emoji_name}: in Buzz"
+  echo "${emoji_name} → registered as :${buzz_shortcode}: in Buzz"
 done
