Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 148 lines 5.0 kB view raw
1#!/usr/bin/env node 2/** 3 * Update the 5 tool sketchbooks by @fifi 4 * - Fix description (no whistlegraph stamp) 5 * - Set inventory to 1 6 * - Ensure product_type is set 7 */ 8 9import { readFileSync } from 'fs'; 10import { fileURLToPath } from 'url'; 11import { dirname, join } from 'path'; 12 13const __dirname = dirname(fileURLToPath(import.meta.url)); 14const envPath = join(__dirname, '../aesthetic-computer-vault/shop/.env'); 15const envContent = readFileSync(envPath, 'utf-8'); 16for (const line of envContent.split('\n')) { 17 if (line && !line.startsWith('#') && line.includes('=')) { 18 const [key, ...valueParts] = line.split('='); 19 process.env[key.trim()] = valueParts.join('=').trim(); 20 } 21} 22 23const ACCESS_TOKEN = process.env.SHOPIFY_ADMIN_ACCESS_TOKEN; 24const BASE_URL = 'https://3pc8se-sj.myshopify.com/admin/api/2024-10'; 25 26// The 5 tools product IDs 27const tools = [ 28 { id: 10307732635829, title: 'I welcome new experiences every single day', code: '25.12.4.11.21' }, 29 { id: 10307732668597, title: 'prompt.ac', code: '25.12.4.11.22' }, 30 { id: 10307732701365, title: 'my songs', code: '25.12.4.11.23' }, 31 { id: 10307732734133, title: 'pretty pictures', code: '25.12.4.11.24' }, 32 { id: 10307732766901, title: 'play', code: '25.12.4.11.25' }, 33]; 34 35// Parse timecode to readable date/time string 36function formatDateFromCode(code) { 37 const [year, month, day, hour, minute] = code.split('.').map(Number); 38 const fullYear = 2000 + year; 39 const months = ['January', 'February', 'March', 'April', 'May', 'June', 40 'July', 'August', 'September', 'October', 'November', 'December']; 41 const monthName = months[month - 1]; 42 43 // Ordinal suffix for day 44 const ordinal = (n) => { 45 if (n > 3 && n < 21) return 'th'; 46 switch (n % 10) { case 1: return 'st'; case 2: return 'nd'; case 3: return 'rd'; default: return 'th'; } 47 }; 48 49 // Format time as 12-hour with AM/PM 50 const period = hour >= 12 ? 'PM' : 'AM'; 51 const hour12 = hour % 12 || 12; 52 const timeStr = `${hour12}:${minute.toString().padStart(2, '0')} ${period}`; 53 54 return `${monthName} ${day}${ordinal(day)}, ${fullYear} at ${timeStr}`; 55} 56 57// Generate description with time from code 58function makeDescription(code) { 59 const dateStr = formatDateFromCode(code); 60 return `<p>Sketchbook customized by <a href="https://aesthetic.computer/@fifi">@fifi</a></p> 61<p>Dated ${dateStr}</p> 62<p>This customized sketchbook ships from Los Angeles and includes a QR code.</p> 63<p>This Midori MD A5 Sketchbook comes with a clear plastic cover and has 48 pages.</p>`; 64} 65 66async function updateProduct(tool) { 67 const description = makeDescription(tool.code); 68 69 // Update product metadata 70 const res = await fetch(`${BASE_URL}/products/${tool.id}.json`, { 71 method: 'PUT', 72 headers: { 73 'X-Shopify-Access-Token': ACCESS_TOKEN, 74 'Content-Type': 'application/json', 75 }, 76 body: JSON.stringify({ 77 product: { 78 id: tool.id, 79 body_html: description, 80 product_type: 'Sketchbook', 81 tags: 'tool, sketchbook, fifi', 82 }, 83 }), 84 }); 85 86 const data = await res.json(); 87 if (data.errors) { 88 console.log('❌ Error updating ' + tool.title + ':', JSON.stringify(data.errors)); 89 return null; 90 } 91 92 console.log('✅ Updated: ' + tool.title); 93 console.log(' Product Type: ' + data.product.product_type); 94 95 // Get the variant ID to update inventory 96 const variantId = data.product.variants[0].id; 97 const inventoryItemId = data.product.variants[0].inventory_item_id; 98 99 console.log(' Variant ID: ' + variantId); 100 console.log(' Inventory Item ID: ' + inventoryItemId); 101 102 return { variantId, inventoryItemId }; 103} 104 105async function setInventory(inventoryItemId, quantity = 1) { 106 // Try to set inventory level 107 const res = await fetch(`${BASE_URL}/inventory_levels/set.json`, { 108 method: 'POST', 109 headers: { 110 'X-Shopify-Access-Token': ACCESS_TOKEN, 111 'Content-Type': 'application/json', 112 }, 113 body: JSON.stringify({ 114 inventory_item_id: inventoryItemId, 115 available: quantity, 116 // We'll need the location_id - let's try without it first 117 }), 118 }); 119 120 const data = await res.json(); 121 if (data.errors) { 122 console.log(' ⚠️ Inventory update needs location_id:', JSON.stringify(data.errors)); 123 return false; 124 } 125 126 console.log(' ✅ Inventory set to ' + quantity); 127 return true; 128} 129 130async function main() { 131 console.log('🛠️ Updating 5 tool sketchbooks by @fifi...\n'); 132 133 for (const tool of tools) { 134 const result = await updateProduct(tool); 135 if (result) { 136 await setInventory(result.inventoryItemId, 1); 137 } 138 console.log(''); 139 await new Promise(r => setTimeout(r, 500)); // Rate limit 140 } 141 142 console.log('✨ Product updates complete!'); 143 console.log('\n⚠️ Note: To set inventory, you may need to:'); 144 console.log(' 1. Add "read_locations" scope to the custom app'); 145 console.log(' 2. Or set inventory manually in Shopify Admin'); 146} 147 148main();