Fix TikTok sync: use jq instead of Python heredoc

Python heredoc was breaking on emoji characters in video titles.
jq handles JSON parsing properly.

Chronicler #76
This commit is contained in:
Claude
2026-04-10 22:08:58 +00:00
parent 34464a91af
commit c0750ea2c1

View File

@@ -1,31 +1,23 @@
#!/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..."
@@ -36,156 +28,82 @@ refresh_token() {
-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"
NEW_ACCESS=$(echo "$RESPONSE" | jq -r '.access_token // empty')
NEW_REFRESH=$(echo "$RESPONSE" | jq -r '.refresh_token // empty')
if [ -z "$NEW_ACCESS" ]; then
echo "ERROR: Token refresh failed"
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)
ACCESS_TOKEN="$NEW_ACCESS"
[ -n "$NEW_REFRESH" ] && REFRESH_TOKEN="$NEW_REFRESH"
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"
echo " Token refreshed"
}
# Function to sync account stats
sync_account_stats() {
echo "Fetching TikTok account stats..."
echo "Syncing 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' \
'https://open.tiktokapis.com/v2/user/info/?fields=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" | jq -r '.data.user.follower_count // 0')
FOLLOWING=$(echo "$RESPONSE" | jq -r '.data.user.following_count // 0')
LIKES=$(echo "$RESPONSE" | jq -r '.data.user.likes_count // 0')
VIDEOS=$(echo "$RESPONSE" | jq -r '.data.user.video_count // 0')
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, Likes: $LIKES, Videos: $VIDEOS"
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
-d "{\"platform\":\"tiktok\",\"handle\":\"playfirefrost\",\"followers\":$FOLLOWERS,\"following\":$FOLLOWING,\"posts\":$VIDEOS}" > /dev/null
echo " Account snapshot synced to Arbiter"
echo " Account snapshot synced"
}
# Function to sync video stats
sync_videos() {
echo "Fetching TikTok video stats..."
echo "Syncing 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' \
'https://open.tiktokapis.com/v2/video/list/?fields=id,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" \
VIDEO_COUNT=$(echo "$RESPONSE" | jq -r '.data.videos | length')
echo " Found $VIDEO_COUNT videos"
echo "$RESPONSE" | jq -c '.data.videos[]' | while read -r video; do
VID=$(echo "$video" | jq -r '.id')
VIEWS=$(echo "$video" | jq -r '.view_count // 0')
LIKES=$(echo "$video" | jq -r '.like_count // 0')
COMMENTS=$(echo "$video" | jq -r '.comment_count // 0')
SHARES=$(echo "$video" | jq -r '.share_count // 0')
POST_URL="https://www.tiktok.com/@playfirefrost/video/$VID"
curl -s -X POST "$ARBITER_URL/sync" \
-H "Authorization: Bearer $ARBITER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"max_count": 50}')
fi
-d "{\"platform\":\"tiktok\",\"post_url\":\"$POST_URL\",\"views\":$VIEWS,\"likes\":$LIKES,\"comments\":$COMMENTS,\"shares\":$SHARES}" > /dev/null
echo " $VID: $VIEWS views, $LIKES likes"
done
# 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
echo " Videos synced"
}
# 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 ==="
echo "=== Complete ==="