Add archived/atomk-to-woocommerce

This commit is contained in:
2026-07-10 16:10:40 +08:00
parent e04866cec5
commit d1750b62ea
@@ -0,0 +1,171 @@
---
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 <caddy_container> cat /etc/caddy/Caddyfile
```
Verify the store:
```bash
curl -s -L https://<domain>/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 <wpcli_container> wp user list --format=csv --fields=user_login,user_email
# Generate WooCommerce API key pair
sudo docker exec <wpcli_container> 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'
```