From faaa4d5fff974e09f96c98a03c9784110e5d8aff Mon Sep 17 00:00:00 2001 From: alectimison-maker Date: Wed, 29 Jul 2026 17:30:40 +0800 Subject: [PATCH] agent: add adapter for Rakuten Ichiba --- src/chrome/src/agent/adapters.js | 14 ++++++++++++ src/firefox/src/agent/adapters.js | 14 ++++++++++++ test/run.js | 38 +++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/src/chrome/src/agent/adapters.js b/src/chrome/src/agent/adapters.js index c271c50c4..dd7c7c118 100644 --- a/src/chrome/src/agent/adapters.js +++ b/src/chrome/src/agent/adapters.js @@ -16294,6 +16294,20 @@ const ADAPTERS = [ - Search hosts (listado.mercadolibre.com.ar, listado.mercadolibre.com.mx, lista.mercadolivre.com.br) can redirect automation to /gz/account-verification. If that wall appears, STOP and ask the user to complete it manually — do not retry, bypass, or claim results were read.`, }, + // ─── Regional — Rakuten Ichiba (Japan) ──────────────────────────────── + { + name: 'rakuten-ichiba', + category: 'general', + matches: (url) => /^https?:\/\/(?:(?:www|search|item)\.rakuten\.co\.jp|basket\.step\.rakuten\.co\.jp)\//.test(url), + notes: ` +- As of 2026-07, Rakuten Ichiba is a MARKETPLACE of independently operated "ショップ" (shops). Stable labels: "買い物かごに入れる" = add to cart, "買い物かご" = cart, and "ご購入手続き" = proceed to checkout. +- Search results marked "[PR]" are sponsored. For a price comparison, use "同じ商品を安い順で見る" when available, then compare the exact variant's "価格+送料" and per-unit price; do not rank the first result or a bare "〜" price range as cheapest. +- Select every size, color, pack count, delivery timing, and other required option before comparing or adding. "定期購入" is a recurring purchase with a different price — keep normal purchase selected unless the user explicitly asks for a subscription. +- The cart is divided into boxes by shop. On desktop, items from different shops cannot share one checkout; mobile "おまとめ購入" may batch the steps, but each shop still creates a separate order and shipment. Read each box and click its own "ご購入手続き". +- Shipping is shop- and destination-specific. A 39 Shop's 3,980円 free-shipping line applies within the same eligible shop and order, not across shops; "特定送料", remote regions, chilled delivery, and large items can remain chargeable. Verify every shop's cart total. +- "ポイント", SPU, and スーパーDEAL are conditional rewards, not an immediate cash-price reduction; campaigns may require entry, membership, payment methods, or have caps, and shipping/tax do not earn ordinary points. Report the payable total separately from expected points.`, + }, + // ─── Regional — Türkiye (TR) ────────────────────────────────────────── // Regional adapters are the project's #1 wanted contribution (CONTRIBUTING.md); // Türkiye is top of the priority list. Add more TR sites (trendyol, diff --git a/src/firefox/src/agent/adapters.js b/src/firefox/src/agent/adapters.js index 2f58e93a0..e2ff4b92c 100644 --- a/src/firefox/src/agent/adapters.js +++ b/src/firefox/src/agent/adapters.js @@ -16292,6 +16292,20 @@ const ADAPTERS = [ - Search hosts (listado.mercadolibre.com.ar, listado.mercadolibre.com.mx, lista.mercadolivre.com.br) can redirect automation to /gz/account-verification. If that wall appears, STOP and ask the user to complete it manually — do not retry, bypass, or claim results were read.`, }, + // ─── Regional — Rakuten Ichiba (Japan) ──────────────────────────────── + { + name: 'rakuten-ichiba', + category: 'general', + matches: (url) => /^https?:\/\/(?:(?:www|search|item)\.rakuten\.co\.jp|basket\.step\.rakuten\.co\.jp)\//.test(url), + notes: ` +- As of 2026-07, Rakuten Ichiba is a MARKETPLACE of independently operated "ショップ" (shops). Stable labels: "買い物かごに入れる" = add to cart, "買い物かご" = cart, and "ご購入手続き" = proceed to checkout. +- Search results marked "[PR]" are sponsored. For a price comparison, use "同じ商品を安い順で見る" when available, then compare the exact variant's "価格+送料" and per-unit price; do not rank the first result or a bare "〜" price range as cheapest. +- Select every size, color, pack count, delivery timing, and other required option before comparing or adding. "定期購入" is a recurring purchase with a different price — keep normal purchase selected unless the user explicitly asks for a subscription. +- The cart is divided into boxes by shop. On desktop, items from different shops cannot share one checkout; mobile "おまとめ購入" may batch the steps, but each shop still creates a separate order and shipment. Read each box and click its own "ご購入手続き". +- Shipping is shop- and destination-specific. A 39 Shop's 3,980円 free-shipping line applies within the same eligible shop and order, not across shops; "特定送料", remote regions, chilled delivery, and large items can remain chargeable. Verify every shop's cart total. +- "ポイント", SPU, and スーパーDEAL are conditional rewards, not an immediate cash-price reduction; campaigns may require entry, membership, payment methods, or have caps, and shipping/tax do not earn ordinary points. Report the payable total separately from expected points.`, + }, + // ─── Regional — Türkiye (TR) ────────────────────────────────────────── // Regional adapters are the project's #1 wanted contribution (CONTRIBUTING.md); // Türkiye is top of the priority list. Add more TR sites (trendyol, diff --git a/test/run.js b/test/run.js index 6ebdaa2c2..e76675777 100644 --- a/test/run.js +++ b/test/run.js @@ -2453,6 +2453,44 @@ test('matches Mercado Libre LATAM storefronts and includes marketplace guidance' assert.equal(firefoxAdapter?.notes, adapter?.notes); }); +test('matches Rakuten Ichiba shopping surfaces and includes marketplace guidance', () => { + const trustedUrls = [ + 'https://www.rakuten.co.jp/', + 'https://search.rakuten.co.jp/search/mall/%E6%B0%B4/', + 'https://item.rakuten.co.jp/example-shop/example-item/', + 'https://basket.step.rakuten.co.jp/rms/mall/bs/cart/', + ]; + for (const url of trustedUrls) { + assert.equal(getActiveAdapter(url)?.name, 'rakuten-ichiba'); + assert.equal(getActiveAdapterFx(url)?.name, 'rakuten-ichiba'); + } + + const rejectedUrls = [ + 'https://travel.rakuten.co.jp/', + 'https://books.rakuten.co.jp/', + 'https://order.step.rakuten.co.jp/', + 'https://www.rakuten.co.jp.phishing.example/', + 'https://example.com/item.rakuten.co.jp/example-shop/example-item/', + ]; + for (const url of rejectedUrls) { + assert.notEqual(getActiveAdapter(url)?.name, 'rakuten-ichiba'); + assert.notEqual(getActiveAdapterFx(url)?.name, 'rakuten-ichiba'); + } + + const adapter = getActiveAdapter('https://search.rakuten.co.jp/search/mall/%E6%B0%B4/'); + const firefoxAdapter = getActiveAdapterFx('https://item.rakuten.co.jp/example-shop/example-item/'); + assert.match(adapter?.notes || '', /2026-07/); + assert.match(adapter?.notes || '', /\[PR\]/); + assert.match(adapter?.notes || '', /同じ商品を安い順で見る/); + assert.match(adapter?.notes || '', /定期購入/); + assert.match(adapter?.notes || '', /different shops/i); + assert.match(adapter?.notes || '', /ご購入手続き/); + assert.match(adapter?.notes || '', /3,980円/); + assert.match(adapter?.notes || '', /特定送料/); + assert.match(adapter?.notes || '', /ポイント/); + assert.equal(firefoxAdapter?.notes, adapter?.notes); +}); + test('matches sahibinden.com and includes anti-bot guidance', () => { assert.equal(getActiveAdapter('https://www.sahibinden.com/')?.name, 'sahibinden'); assert.equal(getActiveAdapter('https://sahibinden.com/kategori/vasita')?.name, 'sahibinden');