#!/bin/bash # /opt/scripts/sync-tiktok.sh # TikTok analytics sync for Firefrost Gaming # Runs via cron, refreshes token automatically, syncs to Arbiter set -e # Config TOKEN_FILE="/opt/scripts/.tiktok-tokens" ARBITER_URL="https://discord-bot.firefrostgaming.com/api/internal/social" ARBITER_TOKEN="6fYF1akCRW6pM2F8n3S3RxeIod4YgRniUJNEQurvBP4=" # TikTok App credentials (sandbox) CLIENT_KEY="sbawse6t5serp8xdqp" CLIENT_SECRET="Ib24OixwLnjZbB8KXAUcYn6ewM60KKDp" # Load existing tokens if [ -f "$TOKEN_FILE" ]; then source "$TOKEN_FILE" else echo "ERROR: Token file not found at $TOKEN_FILE" echo "Create it with:" echo " ACCESS_TOKEN=act.xxxxx" echo " REFRESH_TOKEN=rft.xxxxx" exit 1 fi # Function to refresh access token refresh_token() { echo "Refreshing TikTok access token..." RESPONSE=$(curl -s -X POST 'https://open.tiktokapis.com/v2/oauth/token/' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d "client_key=$CLIENT_KEY" \ -d "client_secret=$CLIENT_SECRET" \ -d "refresh_token=$REFRESH_TOKEN" \ -d 'grant_type=refresh_token') # Check for error ERROR=$(echo "$RESPONSE" | grep -o '"error":"[^"]*"' | cut -d'"' -f4) if [ -n "$ERROR" ] && [ "$ERROR" != "ok" ]; then echo "ERROR: Token refresh failed: $ERROR" echo "$RESPONSE" exit 1 fi # Extract new tokens NEW_ACCESS=$(echo "$RESPONSE" | grep -o '"access_token":"[^"]*"' | cut -d'"' -f4) NEW_REFRESH=$(echo "$RESPONSE" | grep -o '"refresh_token":"[^"]*"' | cut -d'"' -f4) if [ -n "$NEW_ACCESS" ]; then ACCESS_TOKEN="$NEW_ACCESS" echo "Access token refreshed successfully" fi if [ -n "$NEW_REFRESH" ]; then REFRESH_TOKEN="$NEW_REFRESH" fi # Save updated tokens cat > "$TOKEN_FILE" << TOKENS ACCESS_TOKEN="$ACCESS_TOKEN" REFRESH_TOKEN="$REFRESH_TOKEN" TOKENS chmod 600 "$TOKEN_FILE" } # Function to sync account stats sync_account_stats() { echo "Fetching TikTok account stats..." RESPONSE=$(curl -s -X GET \ 'https://open.tiktokapis.com/v2/user/info/?fields=display_name,follower_count,following_count,likes_count,video_count' \ -H "Authorization: Bearer $ACCESS_TOKEN") # Check for auth error (token expired) ERROR=$(echo "$RESPONSE" | grep -o '"code":"[^"]*"' | head -1 | cut -d'"' -f4) if [ "$ERROR" = "access_token_invalid" ]; then echo "Access token expired, refreshing..." refresh_token # Retry RESPONSE=$(curl -s -X GET \ 'https://open.tiktokapis.com/v2/user/info/?fields=display_name,follower_count,following_count,likes_count,video_count' \ -H "Authorization: Bearer $ACCESS_TOKEN") fi FOLLOWERS=$(echo "$RESPONSE" | grep -o '"follower_count":[0-9]*' | cut -d':' -f2) FOLLOWING=$(echo "$RESPONSE" | grep -o '"following_count":[0-9]*' | cut -d':' -f2) LIKES=$(echo "$RESPONSE" | grep -o '"likes_count":[0-9]*' | cut -d':' -f2) VIDEOS=$(echo "$RESPONSE" | grep -o '"video_count":[0-9]*' | cut -d':' -f2) echo " Followers: $FOLLOWERS, Following: $FOLLOWING, Likes: $LIKES, Videos: $VIDEOS" # Sync to Arbiter curl -s -X POST "$ARBITER_URL/snapshot" \ -H "Authorization: Bearer $ARBITER_TOKEN" \ -H "Content-Type: application/json" \ -d "{ \"platform\": \"tiktok\", \"handle\": \"playfirefrost\", \"followers\": ${FOLLOWERS:-0}, \"following\": ${FOLLOWING:-0}, \"posts\": ${VIDEOS:-0} }" > /dev/null echo " Account snapshot synced to Arbiter" } # Function to sync video stats sync_videos() { echo "Fetching TikTok video stats..." RESPONSE=$(curl -s -X POST \ 'https://open.tiktokapis.com/v2/video/list/?fields=id,title,create_time,view_count,like_count,comment_count,share_count' \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"max_count": 50}') # Check for auth error ERROR=$(echo "$RESPONSE" | grep -o '"code":"access_token_invalid"') if [ -n "$ERROR" ]; then echo "Access token expired, refreshing..." refresh_token RESPONSE=$(curl -s -X POST \ 'https://open.tiktokapis.com/v2/video/list/?fields=id,title,create_time,view_count,like_count,comment_count,share_count' \ -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"max_count": 50}') fi # Parse and sync each video using Python for JSON handling python3 << PYTHON import json import subprocess import sys response = '''$RESPONSE''' data = json.loads(response) if 'data' not in data or 'videos' not in data['data']: print(" No videos found or error in response") sys.exit(0) videos = data['data']['videos'] print(f" Found {len(videos)} videos") for video in videos: video_id = video.get('id', '') title = video.get('title', '')[:100] # Truncate for post_url field views = video.get('view_count', 0) likes = video.get('like_count', 0) comments = video.get('comment_count', 0) shares = video.get('share_count', 0) # Create post URL post_url = f"https://www.tiktok.com/@playfirefrost/video/{video_id}" payload = { "platform": "tiktok", "post_url": post_url, "views": views, "likes": likes, "comments": comments, "shares": shares } result = subprocess.run([ 'curl', '-s', '-X', 'POST', '$ARBITER_URL/sync', '-H', 'Authorization: Bearer $ARBITER_TOKEN', '-H', 'Content-Type: application/json', '-d', json.dumps(payload) ], capture_output=True, text=True) print(f" {video_id}: {views} views, {likes} likes") print(" All videos synced to Arbiter") PYTHON } # Main echo "=== TikTok Sync $(date) ===" # Refresh token proactively (do this every run to keep token fresh) refresh_token # Sync data sync_account_stats sync_videos echo "=== Sync complete ==="