fix: avatar support and set it as default
|
|
@ -1,238 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
# Create a stock avatar from uploaded props and set it as server default.
|
|
||||||
#
|
|
||||||
# Usage: ./stock/avatar/create-stock-avatar.sh [--force|-f] [HOST]
|
|
||||||
#
|
|
||||||
# Prerequisites:
|
|
||||||
# 1. Props must be uploaded first: ./stock/avatar/upload-stockavatars.sh
|
|
||||||
# 2. Dev server must be running: ./run-dev.sh -f
|
|
||||||
#
|
|
||||||
# This script:
|
|
||||||
# 1. Queries existing props by slug to get UUIDs
|
|
||||||
# 2. Creates a server avatar with all emotion slots populated
|
|
||||||
# 3. Sets the avatar as the server default for all gender/age combinations
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Parse arguments
|
|
||||||
FORCE=""
|
|
||||||
HOST="http://localhost:3001"
|
|
||||||
DB="chattyness"
|
|
||||||
|
|
||||||
for arg in "$@"; do
|
|
||||||
case "$arg" in
|
|
||||||
--force|-f)
|
|
||||||
FORCE="?force=true"
|
|
||||||
;;
|
|
||||||
http://*)
|
|
||||||
HOST="$arg"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "=========================================="
|
|
||||||
echo "Creating Stock Avatar"
|
|
||||||
echo "=========================================="
|
|
||||||
echo "Host: $HOST"
|
|
||||||
echo "Database: $DB"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Check if server is running
|
|
||||||
echo "Checking server health..."
|
|
||||||
health_response=$(curl -s -o /dev/null -w "%{http_code}" "$HOST/api/admin/health" 2>/dev/null || echo "000")
|
|
||||||
if [ "$health_response" != "200" ]; then
|
|
||||||
echo "ERROR: Server is not responding at $HOST (HTTP $health_response)"
|
|
||||||
echo "Make sure the server is running: ./run-dev.sh -f"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Server is healthy!"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Query prop UUIDs by slug
|
|
||||||
echo "Querying prop UUIDs..."
|
|
||||||
|
|
||||||
get_prop_id() {
|
|
||||||
local slug="$1"
|
|
||||||
psql -d "$DB" -t -A -c "SELECT id FROM server.props WHERE slug = '$slug'" 2>/dev/null | tr -d '[:space:]'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get face prop (skin layer)
|
|
||||||
FACE_ID=$(get_prop_id "face")
|
|
||||||
if [ -z "$FACE_ID" ]; then
|
|
||||||
echo "ERROR: Face prop not found. Run upload-stockavatars.sh first."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo " face: $FACE_ID"
|
|
||||||
|
|
||||||
# Get emotion props
|
|
||||||
NEUTRAL_ID=$(get_prop_id "neutral")
|
|
||||||
SMILE_ID=$(get_prop_id "smile") # This is "happy" emotion
|
|
||||||
SAD_ID=$(get_prop_id "sad")
|
|
||||||
ANGRY_ID=$(get_prop_id "angry")
|
|
||||||
SURPRISED_ID=$(get_prop_id "surprised")
|
|
||||||
THINKING_ID=$(get_prop_id "thinking")
|
|
||||||
LAUGHING_ID=$(get_prop_id "laughing")
|
|
||||||
CRYING_ID=$(get_prop_id "crying")
|
|
||||||
LOVE_ID=$(get_prop_id "love")
|
|
||||||
CONFUSED_ID=$(get_prop_id "confused")
|
|
||||||
SLEEPING_ID=$(get_prop_id "sleeping")
|
|
||||||
WINK_ID=$(get_prop_id "wink")
|
|
||||||
|
|
||||||
# Validate all emotion props exist
|
|
||||||
missing=""
|
|
||||||
[ -z "$NEUTRAL_ID" ] && missing="$missing neutral"
|
|
||||||
[ -z "$SMILE_ID" ] && missing="$missing smile"
|
|
||||||
[ -z "$SAD_ID" ] && missing="$missing sad"
|
|
||||||
[ -z "$ANGRY_ID" ] && missing="$missing angry"
|
|
||||||
[ -z "$SURPRISED_ID" ] && missing="$missing surprised"
|
|
||||||
[ -z "$THINKING_ID" ] && missing="$missing thinking"
|
|
||||||
[ -z "$LAUGHING_ID" ] && missing="$missing laughing"
|
|
||||||
[ -z "$CRYING_ID" ] && missing="$missing crying"
|
|
||||||
[ -z "$LOVE_ID" ] && missing="$missing love"
|
|
||||||
[ -z "$CONFUSED_ID" ] && missing="$missing confused"
|
|
||||||
[ -z "$SLEEPING_ID" ] && missing="$missing sleeping"
|
|
||||||
[ -z "$WINK_ID" ] && missing="$missing wink"
|
|
||||||
|
|
||||||
if [ -n "$missing" ]; then
|
|
||||||
echo "ERROR: Missing emotion props:$missing"
|
|
||||||
echo "Run upload-stockavatars.sh first."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo " neutral: $NEUTRAL_ID"
|
|
||||||
echo " smile (happy): $SMILE_ID"
|
|
||||||
echo " sad: $SAD_ID"
|
|
||||||
echo " angry: $ANGRY_ID"
|
|
||||||
echo " surprised: $SURPRISED_ID"
|
|
||||||
echo " thinking: $THINKING_ID"
|
|
||||||
echo " laughing: $LAUGHING_ID"
|
|
||||||
echo " crying: $CRYING_ID"
|
|
||||||
echo " love: $LOVE_ID"
|
|
||||||
echo " confused: $CONFUSED_ID"
|
|
||||||
echo " sleeping: $SLEEPING_ID"
|
|
||||||
echo " wink: $WINK_ID"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Check if avatar already exists
|
|
||||||
existing_avatar=$(psql -d "$DB" -t -A -c "SELECT id FROM server.avatars WHERE slug = 'stock-avatar'" 2>/dev/null | tr -d '[:space:]')
|
|
||||||
|
|
||||||
if [ -n "$existing_avatar" ] && [ -z "$FORCE" ]; then
|
|
||||||
echo "Stock avatar already exists with ID: $existing_avatar"
|
|
||||||
echo "Use --force to recreate it."
|
|
||||||
AVATAR_ID="$existing_avatar"
|
|
||||||
else
|
|
||||||
# Create the avatar via API
|
|
||||||
echo "Creating stock avatar via API..."
|
|
||||||
|
|
||||||
# Build the JSON payload
|
|
||||||
avatar_json=$(cat <<EOF
|
|
||||||
{
|
|
||||||
"name": "Stock Avatar",
|
|
||||||
"slug": "stock-avatar",
|
|
||||||
"description": "Default stock avatar with all emotion faces",
|
|
||||||
"is_public": true,
|
|
||||||
"l_skin_4": "$FACE_ID",
|
|
||||||
"e_neutral_4": "$NEUTRAL_ID",
|
|
||||||
"e_happy_4": "$SMILE_ID",
|
|
||||||
"e_sad_4": "$SAD_ID",
|
|
||||||
"e_angry_4": "$ANGRY_ID",
|
|
||||||
"e_surprised_4": "$SURPRISED_ID",
|
|
||||||
"e_thinking_4": "$THINKING_ID",
|
|
||||||
"e_laughing_4": "$LAUGHING_ID",
|
|
||||||
"e_crying_4": "$CRYING_ID",
|
|
||||||
"e_love_4": "$LOVE_ID",
|
|
||||||
"e_confused_4": "$CONFUSED_ID",
|
|
||||||
"e_sleeping_4": "$SLEEPING_ID",
|
|
||||||
"e_wink_4": "$WINK_ID"
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
)
|
|
||||||
|
|
||||||
# Delete existing if force mode
|
|
||||||
if [ -n "$existing_avatar" ]; then
|
|
||||||
echo " Deleting existing avatar..."
|
|
||||||
curl -s -X DELETE "$HOST/api/admin/avatars/$existing_avatar" > /dev/null
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create the avatar
|
|
||||||
response=$(curl -s -w "\n%{http_code}" -X POST "$HOST/api/admin/avatars" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "$avatar_json")
|
|
||||||
|
|
||||||
http_code=$(echo "$response" | tail -n1)
|
|
||||||
body=$(echo "$response" | sed '$d')
|
|
||||||
|
|
||||||
if [ "$http_code" = "200" ] || [ "$http_code" = "201" ]; then
|
|
||||||
AVATAR_ID=$(echo "$body" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
|
||||||
echo " ✓ Created avatar: $AVATAR_ID"
|
|
||||||
else
|
|
||||||
echo " ✗ Failed to create avatar (HTTP $http_code): $body"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Set as server default for all gender/age combinations
|
|
||||||
echo "Setting stock avatar as server defaults..."
|
|
||||||
|
|
||||||
psql -d "$DB" -c "
|
|
||||||
UPDATE server.config SET
|
|
||||||
default_avatar_neutral_child = '$AVATAR_ID',
|
|
||||||
default_avatar_neutral_adult = '$AVATAR_ID',
|
|
||||||
default_avatar_male_child = '$AVATAR_ID',
|
|
||||||
default_avatar_male_adult = '$AVATAR_ID',
|
|
||||||
default_avatar_female_child = '$AVATAR_ID',
|
|
||||||
default_avatar_female_adult = '$AVATAR_ID',
|
|
||||||
updated_at = now()
|
|
||||||
WHERE id = '00000000-0000-0000-0000-000000000001'
|
|
||||||
" > /dev/null
|
|
||||||
|
|
||||||
echo " ✓ Set all 6 default avatar columns"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Verify
|
|
||||||
echo "=========================================="
|
|
||||||
echo "Verification"
|
|
||||||
echo "=========================================="
|
|
||||||
|
|
||||||
# Check avatar slots
|
|
||||||
echo "Avatar emotion slots populated:"
|
|
||||||
psql -d "$DB" -t -c "
|
|
||||||
SELECT
|
|
||||||
CASE WHEN l_skin_4 IS NOT NULL THEN '✓' ELSE '✗' END || ' body (l_skin_4)',
|
|
||||||
CASE WHEN e_neutral_4 IS NOT NULL THEN '✓' ELSE '✗' END || ' neutral',
|
|
||||||
CASE WHEN e_happy_4 IS NOT NULL THEN '✓' ELSE '✗' END || ' happy',
|
|
||||||
CASE WHEN e_sad_4 IS NOT NULL THEN '✓' ELSE '✗' END || ' sad',
|
|
||||||
CASE WHEN e_angry_4 IS NOT NULL THEN '✓' ELSE '✗' END || ' angry',
|
|
||||||
CASE WHEN e_surprised_4 IS NOT NULL THEN '✓' ELSE '✗' END || ' surprised',
|
|
||||||
CASE WHEN e_thinking_4 IS NOT NULL THEN '✓' ELSE '✗' END || ' thinking',
|
|
||||||
CASE WHEN e_laughing_4 IS NOT NULL THEN '✓' ELSE '✗' END || ' laughing',
|
|
||||||
CASE WHEN e_crying_4 IS NOT NULL THEN '✓' ELSE '✗' END || ' crying',
|
|
||||||
CASE WHEN e_love_4 IS NOT NULL THEN '✓' ELSE '✗' END || ' love',
|
|
||||||
CASE WHEN e_confused_4 IS NOT NULL THEN '✓' ELSE '✗' END || ' confused',
|
|
||||||
CASE WHEN e_sleeping_4 IS NOT NULL THEN '✓' ELSE '✗' END || ' sleeping',
|
|
||||||
CASE WHEN e_wink_4 IS NOT NULL THEN '✓' ELSE '✗' END || ' wink'
|
|
||||||
FROM server.avatars WHERE slug = 'stock-avatar'
|
|
||||||
" | tr '|' '\n' | grep -v '^$' | sed 's/^ */ /'
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Check server defaults
|
|
||||||
echo "Server config defaults:"
|
|
||||||
psql -d "$DB" -t -c "
|
|
||||||
SELECT
|
|
||||||
CASE WHEN default_avatar_neutral_adult IS NOT NULL THEN '✓' ELSE '✗' END || ' default_avatar_neutral_adult',
|
|
||||||
CASE WHEN default_avatar_neutral_child IS NOT NULL THEN '✓' ELSE '✗' END || ' default_avatar_neutral_child',
|
|
||||||
CASE WHEN default_avatar_male_adult IS NOT NULL THEN '✓' ELSE '✗' END || ' default_avatar_male_adult',
|
|
||||||
CASE WHEN default_avatar_male_child IS NOT NULL THEN '✓' ELSE '✗' END || ' default_avatar_male_child',
|
|
||||||
CASE WHEN default_avatar_female_adult IS NOT NULL THEN '✓' ELSE '✗' END || ' default_avatar_female_adult',
|
|
||||||
CASE WHEN default_avatar_female_child IS NOT NULL THEN '✓' ELSE '✗' END || ' default_avatar_female_child'
|
|
||||||
FROM server.config WHERE id = '00000000-0000-0000-0000-000000000001'
|
|
||||||
" | tr '|' '\n' | grep -v '^$' | sed 's/^ */ /'
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "=========================================="
|
|
||||||
echo "Stock avatar setup complete!"
|
|
||||||
echo "Avatar ID: $AVATAR_ID"
|
|
||||||
echo "=========================================="
|
|
||||||
299
stock/avatar/setup.sh
Executable file
|
|
@ -0,0 +1,299 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Set up stock avatar: upload props, create avatar, set as server default.
|
||||||
|
#
|
||||||
|
# Usage: ./stock/avatar/setup.sh [--force|-f] [HOST]
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# --force, -f Update existing props/avatar instead of failing on conflict
|
||||||
|
#
|
||||||
|
# HOST defaults to http://localhost:3001 (owner admin port)
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# 1. Run the dev server: ./run-dev.sh -f
|
||||||
|
# 2. Wait for it to finish building: ./run-dev.sh -s
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Parse arguments
|
||||||
|
FORCE=""
|
||||||
|
HOST="http://localhost:3001"
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--force|-f)
|
||||||
|
FORCE="?force=true"
|
||||||
|
;;
|
||||||
|
http://*)
|
||||||
|
HOST="$arg"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Stock Avatar Setup"
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Host: $HOST"
|
||||||
|
echo "Source: $SCRIPT_DIR"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if server is running
|
||||||
|
echo "Checking server health..."
|
||||||
|
health_response=$(curl -s -o /dev/null -w "%{http_code}" "$HOST/api/admin/health" 2>/dev/null || echo "000")
|
||||||
|
if [ "$health_response" != "200" ]; then
|
||||||
|
echo "ERROR: Server is not responding at $HOST (HTTP $health_response)"
|
||||||
|
echo ""
|
||||||
|
echo "Make sure the server is running:"
|
||||||
|
echo " ./run-dev.sh -f"
|
||||||
|
echo " ./run-dev.sh -s # Check status"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Server is healthy!"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Step 1: Upload Props
|
||||||
|
# =============================================================================
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Step 1: Uploading Props"
|
||||||
|
echo "=========================================="
|
||||||
|
|
||||||
|
capitalize() {
|
||||||
|
echo "$1" | sed 's/.*/\u&/'
|
||||||
|
}
|
||||||
|
|
||||||
|
get_tags() {
|
||||||
|
local filename="$1"
|
||||||
|
case "$filename" in
|
||||||
|
face.svg)
|
||||||
|
echo '["base", "face"]'
|
||||||
|
;;
|
||||||
|
neutral.svg|smile.svg|sad.svg|angry.svg|surprised.svg|thinking.svg|laughing.svg|crying.svg|love.svg|confused.svg|sleeping.svg|wink.svg)
|
||||||
|
echo '["face"]'
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo '["prop"]'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
get_positioning() {
|
||||||
|
local filename="$1"
|
||||||
|
case "$filename" in
|
||||||
|
face.svg)
|
||||||
|
echo "layer:skin"
|
||||||
|
;;
|
||||||
|
neutral.svg|smile.svg|sad.svg|angry.svg|surprised.svg|thinking.svg|laughing.svg|crying.svg|love.svg|confused.svg|sleeping.svg|wink.svg)
|
||||||
|
echo "layer:emote"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "none"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
upload_count=0
|
||||||
|
upload_fail=0
|
||||||
|
|
||||||
|
for file in "$SCRIPT_DIR"/*.svg; do
|
||||||
|
if [ ! -f "$file" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
filename=$(basename "$file")
|
||||||
|
name_without_ext="${filename%.svg}"
|
||||||
|
display_name=$(capitalize "$name_without_ext")
|
||||||
|
tags=$(get_tags "$filename")
|
||||||
|
positioning=$(get_positioning "$filename")
|
||||||
|
|
||||||
|
echo " $filename -> $display_name"
|
||||||
|
|
||||||
|
positioning_type="${positioning%%:*}"
|
||||||
|
positioning_value="${positioning#*:}"
|
||||||
|
|
||||||
|
case "$positioning_type" in
|
||||||
|
layer)
|
||||||
|
positioning_json="\"default_layer\": \"$positioning_value\", \"default_position\": 4"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
positioning_json=""
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -n "$positioning_json" ]; then
|
||||||
|
metadata="{\"name\": \"$display_name\", \"tags\": $tags, $positioning_json}"
|
||||||
|
else
|
||||||
|
metadata="{\"name\": \"$display_name\", \"tags\": $tags}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
response=$(curl -s -w "\n%{http_code}" -X POST "$HOST/api/admin/props$FORCE" \
|
||||||
|
-F "metadata=$metadata" \
|
||||||
|
-F "file=@$file")
|
||||||
|
|
||||||
|
http_code=$(echo "$response" | tail -n1)
|
||||||
|
|
||||||
|
if [ "$http_code" = "200" ] || [ "$http_code" = "201" ]; then
|
||||||
|
echo " ✓ Uploaded"
|
||||||
|
((++upload_count))
|
||||||
|
else
|
||||||
|
body=$(echo "$response" | sed '$d')
|
||||||
|
echo " ✗ Failed (HTTP $http_code): $body"
|
||||||
|
((++upload_fail))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Props: $upload_count uploaded, $upload_fail failed"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ "$upload_fail" -gt 0 ] && [ -z "$FORCE" ]; then
|
||||||
|
echo "Some props failed. Use --force to update existing props."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Step 2: Query Prop UUIDs
|
||||||
|
# =============================================================================
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Step 2: Creating Avatar"
|
||||||
|
echo "=========================================="
|
||||||
|
|
||||||
|
# Query prop UUID by slug via API
|
||||||
|
get_prop_id() {
|
||||||
|
local slug="$1"
|
||||||
|
curl -s "$HOST/api/admin/props/by-slug/$slug" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Querying prop UUIDs..."
|
||||||
|
|
||||||
|
FACE_ID=$(get_prop_id "face")
|
||||||
|
if [ -z "$FACE_ID" ]; then
|
||||||
|
echo "ERROR: Face prop not found."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
NEUTRAL_ID=$(get_prop_id "neutral")
|
||||||
|
SMILE_ID=$(get_prop_id "smile")
|
||||||
|
SAD_ID=$(get_prop_id "sad")
|
||||||
|
ANGRY_ID=$(get_prop_id "angry")
|
||||||
|
SURPRISED_ID=$(get_prop_id "surprised")
|
||||||
|
THINKING_ID=$(get_prop_id "thinking")
|
||||||
|
LAUGHING_ID=$(get_prop_id "laughing")
|
||||||
|
CRYING_ID=$(get_prop_id "crying")
|
||||||
|
LOVE_ID=$(get_prop_id "love")
|
||||||
|
CONFUSED_ID=$(get_prop_id "confused")
|
||||||
|
SLEEPING_ID=$(get_prop_id "sleeping")
|
||||||
|
WINK_ID=$(get_prop_id "wink")
|
||||||
|
|
||||||
|
# Validate all props exist
|
||||||
|
missing=""
|
||||||
|
[ -z "$NEUTRAL_ID" ] && missing="$missing neutral"
|
||||||
|
[ -z "$SMILE_ID" ] && missing="$missing smile"
|
||||||
|
[ -z "$SAD_ID" ] && missing="$missing sad"
|
||||||
|
[ -z "$ANGRY_ID" ] && missing="$missing angry"
|
||||||
|
[ -z "$SURPRISED_ID" ] && missing="$missing surprised"
|
||||||
|
[ -z "$THINKING_ID" ] && missing="$missing thinking"
|
||||||
|
[ -z "$LAUGHING_ID" ] && missing="$missing laughing"
|
||||||
|
[ -z "$CRYING_ID" ] && missing="$missing crying"
|
||||||
|
[ -z "$LOVE_ID" ] && missing="$missing love"
|
||||||
|
[ -z "$CONFUSED_ID" ] && missing="$missing confused"
|
||||||
|
[ -z "$SLEEPING_ID" ] && missing="$missing sleeping"
|
||||||
|
[ -z "$WINK_ID" ] && missing="$missing wink"
|
||||||
|
|
||||||
|
if [ -n "$missing" ]; then
|
||||||
|
echo "ERROR: Missing props:$missing"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " ✓ All 13 props found"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Step 3: Create Avatar
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Check if avatar already exists
|
||||||
|
existing_avatar=$(curl -s "$HOST/api/admin/avatars/by-slug/stock-avatar" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||||
|
|
||||||
|
if [ -n "$existing_avatar" ] && [ -z "$FORCE" ]; then
|
||||||
|
echo "Stock avatar already exists: $existing_avatar"
|
||||||
|
echo "Use --force to recreate."
|
||||||
|
AVATAR_ID="$existing_avatar"
|
||||||
|
else
|
||||||
|
avatar_json=$(cat <<EOF
|
||||||
|
{
|
||||||
|
"name": "Stock Avatar",
|
||||||
|
"slug": "stock-avatar",
|
||||||
|
"description": "Default stock avatar with all emotion faces",
|
||||||
|
"is_public": true,
|
||||||
|
"l_skin_4": "$FACE_ID",
|
||||||
|
"e_neutral_4": "$NEUTRAL_ID",
|
||||||
|
"e_happy_4": "$SMILE_ID",
|
||||||
|
"e_sad_4": "$SAD_ID",
|
||||||
|
"e_angry_4": "$ANGRY_ID",
|
||||||
|
"e_surprised_4": "$SURPRISED_ID",
|
||||||
|
"e_thinking_4": "$THINKING_ID",
|
||||||
|
"e_laughing_4": "$LAUGHING_ID",
|
||||||
|
"e_crying_4": "$CRYING_ID",
|
||||||
|
"e_love_4": "$LOVE_ID",
|
||||||
|
"e_confused_4": "$CONFUSED_ID",
|
||||||
|
"e_sleeping_4": "$SLEEPING_ID",
|
||||||
|
"e_wink_4": "$WINK_ID"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ -n "$existing_avatar" ]; then
|
||||||
|
echo "Deleting existing avatar..."
|
||||||
|
curl -s -X DELETE "$HOST/api/admin/avatars/$existing_avatar" > /dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
response=$(curl -s -w "\n%{http_code}" -X POST "$HOST/api/admin/avatars" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$avatar_json")
|
||||||
|
|
||||||
|
http_code=$(echo "$response" | tail -n1)
|
||||||
|
body=$(echo "$response" | sed '$d')
|
||||||
|
|
||||||
|
if [ "$http_code" = "200" ] || [ "$http_code" = "201" ]; then
|
||||||
|
AVATAR_ID=$(echo "$body" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||||
|
echo " ✓ Created avatar: $AVATAR_ID"
|
||||||
|
else
|
||||||
|
echo " ✗ Failed (HTTP $http_code): $body"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Step 4: Set as Server Default
|
||||||
|
# =============================================================================
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Step 3: Setting Server Defaults"
|
||||||
|
echo "=========================================="
|
||||||
|
|
||||||
|
response=$(curl -s -w "\n%{http_code}" -X PATCH "$HOST/api/admin/config/default-avatars" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{
|
||||||
|
\"default_avatar_neutral_child\": \"$AVATAR_ID\",
|
||||||
|
\"default_avatar_neutral_adult\": \"$AVATAR_ID\",
|
||||||
|
\"default_avatar_male_child\": \"$AVATAR_ID\",
|
||||||
|
\"default_avatar_male_adult\": \"$AVATAR_ID\",
|
||||||
|
\"default_avatar_female_child\": \"$AVATAR_ID\",
|
||||||
|
\"default_avatar_female_adult\": \"$AVATAR_ID\"
|
||||||
|
}")
|
||||||
|
|
||||||
|
http_code=$(echo "$response" | tail -n1)
|
||||||
|
if [ "$http_code" = "200" ]; then
|
||||||
|
echo " ✓ Set all 6 default avatar columns"
|
||||||
|
else
|
||||||
|
echo " ✗ Failed (HTTP $http_code)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Stock Avatar Setup Complete!"
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Avatar ID: $AVATAR_ID"
|
||||||
|
echo ""
|
||||||
|
|
@ -1,174 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
# Upload all stock avatar props to the server.
|
|
||||||
#
|
|
||||||
# Usage: ./stockavatar/upload-stockavatars.sh [--force|-f] [HOST]
|
|
||||||
#
|
|
||||||
# Options:
|
|
||||||
# --force, -f Update existing props instead of failing with 409 Conflict
|
|
||||||
#
|
|
||||||
# HOST defaults to http://localhost:3001 (owner admin port)
|
|
||||||
#
|
|
||||||
# Prerequisites:
|
|
||||||
# 1. Run the dev server: ./run-dev.sh -f
|
|
||||||
# 2. Wait for it to finish building: ./run-dev.sh -s
|
|
||||||
#
|
|
||||||
# The owner admin server (port 3001) uses the chattyness_owner DB role
|
|
||||||
# which bypasses RLS, so no authentication is required.
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Parse arguments
|
|
||||||
FORCE=""
|
|
||||||
HOST="http://localhost:3001"
|
|
||||||
|
|
||||||
for arg in "$@"; do
|
|
||||||
case "$arg" in
|
|
||||||
--force|-f)
|
|
||||||
FORCE="?force=true"
|
|
||||||
;;
|
|
||||||
http://*)
|
|
||||||
HOST="$arg"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
# Script directory is the stockavatar directory
|
|
||||||
STOCKAVATAR_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
|
|
||||||
echo "Uploading stock avatars to $HOST/api/admin/props"
|
|
||||||
echo "Source directory: $STOCKAVATAR_DIR"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Check if server is running
|
|
||||||
echo "Checking server health..."
|
|
||||||
health_response=$(curl -s -o /dev/null -w "%{http_code}" "$HOST/api/admin/health" 2>/dev/null || echo "000")
|
|
||||||
if [ "$health_response" != "200" ]; then
|
|
||||||
echo "ERROR: Server is not responding at $HOST (HTTP $health_response)"
|
|
||||||
echo ""
|
|
||||||
echo "Make sure the server is running:"
|
|
||||||
echo " ./run-dev.sh -f"
|
|
||||||
echo " ./run-dev.sh -s # Check status"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Server is healthy!"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Function to capitalize first letter
|
|
||||||
capitalize() {
|
|
||||||
echo "$1" | sed 's/.*/\u&/'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to determine tags based on filename
|
|
||||||
# Tags complement default_layer - avoid redundant info
|
|
||||||
get_tags() {
|
|
||||||
local filename="$1"
|
|
||||||
case "$filename" in
|
|
||||||
face.svg)
|
|
||||||
# Base face prop - "skin" is already in default_layer
|
|
||||||
echo '["base", "face"]'
|
|
||||||
;;
|
|
||||||
neutral.svg | smile.svg | sad.svg | angry.svg | surprised.svg | thinking.svg | laughing.svg | crying.svg | love.svg | confused.svg | sleeping.svg | wink.svg)
|
|
||||||
# Facial expression props - "emote" is already in default_layer
|
|
||||||
echo '["face"]'
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo '["prop"]'
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to get positioning fields based on filename
|
|
||||||
# Returns: "layer:<value>" for content layer props, "none" for generic props
|
|
||||||
get_positioning() {
|
|
||||||
local filename="$1"
|
|
||||||
case "$filename" in
|
|
||||||
face.svg)
|
|
||||||
# Base face is a content layer prop (skin layer)
|
|
||||||
echo "layer:skin"
|
|
||||||
;;
|
|
||||||
neutral.svg | smile.svg | sad.svg | angry.svg | surprised.svg | thinking.svg | laughing.svg | crying.svg | love.svg | confused.svg | sleeping.svg | wink.svg)
|
|
||||||
# Facial expression props use the emote layer
|
|
||||||
echo "layer:emote"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "none"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
# Track success/failure counts
|
|
||||||
success_count=0
|
|
||||||
fail_count=0
|
|
||||||
|
|
||||||
# Upload each SVG file
|
|
||||||
for file in "$STOCKAVATAR_DIR"/*.svg; do
|
|
||||||
if [ ! -f "$file" ]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
filename=$(basename "$file")
|
|
||||||
name_without_ext="${filename%.svg}"
|
|
||||||
display_name=$(capitalize "$name_without_ext")
|
|
||||||
tags=$(get_tags "$filename")
|
|
||||||
positioning=$(get_positioning "$filename")
|
|
||||||
|
|
||||||
echo "Uploading: $filename -> $display_name (positioning: $positioning)"
|
|
||||||
|
|
||||||
# Build positioning fields based on type
|
|
||||||
positioning_type="${positioning%%:*}"
|
|
||||||
positioning_value="${positioning#*:}"
|
|
||||||
|
|
||||||
case "$positioning_type" in
|
|
||||||
layer)
|
|
||||||
# Content layer prop (skin, clothes, accessories, emote)
|
|
||||||
positioning_json="\"default_layer\": \"$positioning_value\", \"default_position\": 4"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# Generic prop (no default positioning)
|
|
||||||
positioning_json=""
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Create metadata JSON
|
|
||||||
if [ -n "$positioning_json" ]; then
|
|
||||||
metadata=$(
|
|
||||||
cat <<EOF
|
|
||||||
{
|
|
||||||
"name": "$display_name",
|
|
||||||
"tags": $tags,
|
|
||||||
$positioning_json
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
)
|
|
||||||
else
|
|
||||||
metadata=$(
|
|
||||||
cat <<EOF
|
|
||||||
{
|
|
||||||
"name": "$display_name",
|
|
||||||
"tags": $tags
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Upload via curl
|
|
||||||
response=$(curl -s -w "\n%{http_code}" -X POST "$HOST/api/admin/props$FORCE" \
|
|
||||||
-F "metadata=$metadata" \
|
|
||||||
-F "file=@$file")
|
|
||||||
|
|
||||||
# Extract HTTP status code (last line)
|
|
||||||
http_code=$(echo "$response" | tail -n1)
|
|
||||||
body=$(echo "$response" | sed '$d')
|
|
||||||
|
|
||||||
if [ "$http_code" = "200" ] || [ "$http_code" = "201" ]; then
|
|
||||||
echo " ✓ Success: $body"
|
|
||||||
((++success_count))
|
|
||||||
else
|
|
||||||
echo " ✗ Failed (HTTP $http_code): $body"
|
|
||||||
((++fail_count))
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "=========================================="
|
|
||||||
echo "Upload complete: $success_count succeeded, $fail_count failed"
|
|
||||||
|
|
@ -559,8 +559,8 @@
|
||||||
tea: ['iced', 'pot', 'cup', 'cup-empty', 'bag'],
|
tea: ['iced', 'pot', 'cup', 'cup-empty', 'bag'],
|
||||||
misc: ['iou', 'signed-dollar', 'thankyou', 'yousuck', 'businesscard'],
|
misc: ['iou', 'signed-dollar', 'thankyou', 'yousuck', 'businesscard'],
|
||||||
goodpol: ['cccp', 'china', 'palestine'],
|
goodpol: ['cccp', 'china', 'palestine'],
|
||||||
screen: ['projector', 'projector-with-stand'],
|
screen: ['projector-screen', 'projector-screen-with-stand', 'projector-remote-control'],
|
||||||
keyboard: ['media']
|
keyboard: ['standard']
|
||||||
};
|
};
|
||||||
|
|
||||||
// Flags
|
// Flags
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,5 @@ for arg in "$@"; do
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
(cd "$SCRIPT_DIR/avatar" && ./upload-stockavatars.sh $FORCE_FLAG)
|
(cd "$SCRIPT_DIR/avatar" && ./setup.sh $FORCE_FLAG)
|
||||||
(cd "$SCRIPT_DIR/props" && ./upload-stockprops.sh $FORCE_FLAG)
|
(cd "$SCRIPT_DIR/props" && ./upload-stockprops.sh $FORCE_FLAG)
|
||||||
|
|
||||||
# Create stock avatar from uploaded props and set as default
|
|
||||||
(cd "$SCRIPT_DIR/avatar" && ./create-stock-avatar.sh $FORCE_FLAG)
|
|
||||||
|
|
|
||||||
67
stock/props/keyboard-standard.svg
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120" width="120" height="120">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="kbBody" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||||
|
<stop offset="0%" stop-color="#4a4a4a"/>
|
||||||
|
<stop offset="100%" stop-color="#2a2a2a"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="kbKey" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||||
|
<stop offset="0%" stop-color="#5a5a5a"/>
|
||||||
|
<stop offset="100%" stop-color="#3a3a3a"/>
|
||||||
|
</linearGradient>
|
||||||
|
<filter id="kbShadow" x="-10%" y="-10%" width="120%" height="130%">
|
||||||
|
<feDropShadow dx="0" dy="1" stdDeviation="0.5" flood-color="#000000" flood-opacity="0.4"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- Keyboard body -->
|
||||||
|
<rect x="5" y="45" width="110" height="45" rx="4" fill="url(#kbBody)"/>
|
||||||
|
<rect x="5" y="45" width="110" height="45" rx="4" fill="none" stroke="#222" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Row 1: Number keys -->
|
||||||
|
<rect x="10" y="50" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="19" y="50" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="28" y="50" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="37" y="50" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="46" y="50" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="55" y="50" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="64" y="50" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="73" y="50" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="82" y="50" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="91" y="50" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="100" y="50" width="10" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
|
||||||
|
<!-- Row 2: QWERTY row -->
|
||||||
|
<rect x="10" y="59" width="10" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="22" y="59" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="31" y="59" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="40" y="59" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="49" y="59" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="58" y="59" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="67" y="59" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="76" y="59" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="85" y="59" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="94" y="59" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="103" y="59" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
|
||||||
|
<!-- Row 3: Home row -->
|
||||||
|
<rect x="10" y="68" width="12" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="24" y="68" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="33" y="68" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="42" y="68" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="51" y="68" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="60" y="68" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="69" y="68" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="78" y="68" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="87" y="68" width="7" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="96" y="68" width="14" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
|
||||||
|
<!-- Row 4: Bottom row with spacebar -->
|
||||||
|
<rect x="10" y="77" width="10" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="22" y="77" width="10" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="34" y="77" width="10" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<!-- Spacebar -->
|
||||||
|
<rect x="46" y="77" width="32" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="80" y="77" width="10" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="92" y="77" width="8" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
<rect x="102" y="77" width="8" height="7" rx="1" fill="url(#kbKey)" filter="url(#kbShadow)"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1 KiB |