--- name: atomk-to-woocommerce category: cross-border-ecommerce description: Claim products from AtomK, generate AI listing data, and publish them to WooCommerce via REST API. --- # AtomK → WooCommerce Product Publishing Automates the full pipeline: log into AtomK, claim products from the Product Pool, generate AI listing data, and publish them to an existing WooCommerce store via its REST API. ## Prerequisites - Node.js Playwright at `/home/ubuntu/.hermes/hermes-agent/node_modules/playwright` - `sharp` npm package installed for image resizing - Python with `requests` and `Pillow` packages - AtomK credentials: admincao / Tt123456! - An existing WooCommerce instance (e.g. `us1.atomk.cn`) ## Step 1: Claim Products from AtomK & Generate AI Data Use the `atomk-bulk-claim` skill approach. Key points for this workflow: ```bash export NODE_PATH=/home/ubuntu/.hermes/hermes-agent/node_modules node /path/to/atomk_claim_script.js ``` Script essentials: - Login at `https://atomlisting.com/login` (press Enter on password field, do NOT click login button) - Navigate to `https://atomlisting.com/products` - Select category via `select` element (e.g. `家居百货`) - Click `认领并编辑` buttons - For each claimed product (URL contains `/editor/{id}`): - Click a platform tab (速卖通 / Temu / 亚马逊 / TikTok Shop / etc.) - Click `AI 生成` and poll until text no longer contains `生成中` - Extract: title, description, price, keywords, image URLs, weight/dimensions - Save results to JSON (e.g. `/tmp/atomk_claimed_products.json`) ## Step 2: Prepare WooCommerce API Access ### Discover the WooCommerce instance Check for existing containers: ```bash sudo docker ps --filter name=wordpress ``` Check Caddyfile for domain: ```bash sudo docker exec cat /etc/caddy/Caddyfile ``` Verify the store: ```bash curl -s -L https:///wp-json/ | head -5 ``` ### Generate API Keys programmatically **Critical**: Find the actual admin username first — it may NOT be `admin`. ```bash # List WP users sudo docker exec wp user list --format=csv --fields=user_login,user_email # Generate WooCommerce API key pair sudo docker exec wp eval ' if (function_exists("wc_rand_hash")) { $u = get_user_by("login","admincao"); # <-- use actual username if ($u) { global $wpdb; $ck = "ck_" . wc_rand_hash(); $cs = "cs_" . wc_rand_hash(); $wpdb->insert($wpdb->prefix . "woocommerce_api_keys", [ "user_id" => $u->ID, "description" => "Auto API Key", "permissions" => "read_write", "consumer_key" => wc_api_hash($ck), "consumer_secret" => $cs, "truncated_key" => substr($ck, -7) ]); echo "CONSUMER_KEY:" . $ck . "\nCONSUMER_SECRET:" . $cs . "\n"; } else { echo "user not found\n"; } } else { echo "wc functions not found\n"; } ' ``` ## Step 3: Publish to WooCommerce ### Python publisher script ```python import json, os, requests from io import BytesIO from PIL import Image WC_URL = "https://us1.atomk.cn/wp-json/wc/v3" CK = "ck_..." CS = "cs_..." def resize_image(data): img = Image.open(BytesIO(data)).convert("RGB") img = img.resize((800, 800), Image.LANCZOS) out = BytesIO() img.save(out, format="JPEG", quality=90) out.seek(0) return out.read() def create_product(data, image_url): url = f"{WC_URL}/products" payload = { "name": data.get("title", data.get("productCode")), "type": "simple", "regular_price": data.get("price", "10.00"), "description": data.get("description", ""), "short_description": data.get("keywords", ""), "images": [{"src": image_url}], "manage_stock": False, "stock_status": "instock", "status": "publish", "tags": [{"name": t.strip()} for t in data.get("keywords", "").split(",") if t.strip()] } r = requests.post(url, auth=(CK, CS), json=payload) if r.status_code in (200, 201): return r.json().get("id"), r.json().get("permalink") else: print(f"Failed: {r.status_code} {r.text[:500]}") return None, None def main(): with open("/tmp/atomk_claimed_products.json") as f: products = json.load(f) for p in products: code = p.get("productCode") imgs = p.get("images", []) if not imgs: continue # Download first image r = requests.get(imgs[0], timeout=30) r.raise_for_status() # Resize resized = resize_image(r.content) # Save locally path = f"/tmp/wc_images/{code}_800x800.jpg" os.makedirs("/tmp/wc_images", exist_ok=True) with open(path, "wb") as f: f.write(resized) # Publish (use external image URL; media upload via WP REST often 401 with WC creds) pid, url = create_product(p, imgs[0]) print(f"Created {code} -> ID={pid} {url}") if __name__ == "__main__": main() ``` ## Pitfalls & Lessons Learned - **WP admin username discovery**: The WooCommerce container on this system uses `admincao` as the WP admin, not `admin`. Always run `wp user list` first. - **WP REST media upload 401**: Uploading images via `/wp-json/wp/v2/media` using WooCommerce consumer key/secret often fails with `invalid_username`. The workaround is to pass the external image URL directly in the product `images` array as `{ "src": "https://..." }`; WooCommerce will download and attach it automatically. - **Image resizing**: AtomK images may be large or in webp format. Always convert to 800x800 JPEG before referencing, or let WooCommerce handle the external URL and resize server-side. - **HTTPS cert**: atomlisting.com has a cert mismatch; `ignoreHTTPSErrors: true` is required at both `chromium.launch()` and `browser.newContext()` levels. - **Login trap**: The login form has `method="get"`. Clicking the button submits GET to `login?` and stays on the page. Press Enter on the password field to trigger the correct JS POST handler. - **Platform tab order**: In the AtomK editor, click the platform tab FIRST, THEN click `AI 生成`. The button text changes to reflect the selected platform. ## Verification List published products: ```bash curl -s -u 'CK:CS' 'https://us1.atomk.cn/wp-json/wc/v3/products?per_page=10' ```