Add archived/atomk-to-woocommerce
This commit is contained in:
@@ -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'
|
||||
```
|
||||
Reference in New Issue
Block a user