appstoreprice-hub · diff
git:20260627.4feeac4 to git:20260801.3cb01ce
46 added, 47 removed. Audit A to A.
---
name: appstoreprice-hub
description: >-
- 查询 App Store 全球各地区应用价格的技能,通过 appstoreprice.org 获取数据。
- 使用 Minis 内置浏览器(minis-browser-use CLI)在页面上下文中直接调用网站原生签名函数,
- 无需 API Key、无需自行实现签名算法。支持:按名称搜索应用、查询单个 App 所有地区价格、
- 获取最便宜地区排行、分页浏览应用列表。
- 当用户提到"App Store 价格"、"哪个区最便宜"、"土耳其区价格"、"appstoreprice"、
- "app 比价"、"App Store 低价区"、"订阅哪个区划算",或任何需要查询 iOS/macOS App
- 跨地区价格对比的场景,必须触发本技能。
+ A skill for querying App Store app prices across regions worldwide, using data from appstoreprice.org.
+ It uses the Minis built-in browser (minis-browser-use CLI) to call the website's native signing function directly in the page context,
+ with no API key and no need to implement the signing algorithm yourself. It supports searching for apps by name, querying prices for one app across all regions,
+ retrieving rankings of the cheapest regions, and browsing app lists with pagination.
+ This skill must be triggered when the user mentions "App Store prices," "which region is cheapest," "Turkey region price," "appstoreprice,"
+ "app price comparison," "low-price App Store regions," "which subscription region is the best value," or any scenario that requires cross-region price comparisons for iOS/macOS apps.
---
# appstoreprice-hub
- 查询 [appstoreprice.org](https://appstoreprice.org) 的 App Store 全球价格数据。
+ Query App Store global pricing data from [appstoreprice.org](https://appstoreprice.org).
- > **数据来源**:[appstoreprice.org](https://appstoreprice.org),由 [@qingnianxiaozhe](https://x.com/qingnianxiaozhe) 维护的非官方比价网站,实时抓取并对比全球各地区 App Store 价格。非 Apple 官方数据,价格每日更新。
+ > **Data source**: [appstoreprice.org](https://appstoreprice.org), an unofficial price comparison website maintained by [@qingnianxiaozhe](https://x.com/qingnianxiaozhe). It scrapes and compares App Store prices across regions worldwide in real time. This is not official Apple data. Prices are updated daily.
- ## 原理
+ ## How It Works
- 网站为 Next.js App Router,通过两种方式访问数据:
+ The website uses the Next.js App Router and accesses data in two ways:
- 1. **REST API**(搜索/列表):需要 FNV-1a 签名头 `X-Timestamp` + `X-Signature`
- 2. **RSC 页面流**(价格详情):`fetch(url, { headers: { RSC: '1' } })` 直接解析
+ 1. **REST API** (search/list): requires the FNV-1a signature headers `X-Timestamp` + `X-Signature`
+ 2. **RSC page stream** (price details): directly parse `fetch(url, { headers: { RSC: '1' } })`
- 签名函数已内嵌在页面 webpack module(当前为 `22463`)中,直接在网站页面上下文里复用,无需自行实现。module ID 可能随网站部署更新变化,参见故障排查。
+ The signing function is embedded in the page's webpack module (currently `22463`). Reuse it directly in the website page context instead of implementing it yourself. The module ID may change when the website is deployed or updated. See Troubleshooting.
- ## 执行方式:minis-browser-use CLI
+ ## Execution Method: minis-browser-use CLI
- **始终使用 `minis-browser-use` CLI + shell 脚本执行**,不要用 `browser_use` tool call。
+ **Always use the `minis-browser-use` CLI + shell script**. Do not use the `browser_use` tool call.
- 好处:JS 代码从文件读取直接传给进程,不会占用 agent 上下文。
+ Benefit: JS code is read from a file and passed directly to the process, so it does not occupy the agent context.
- ### 标准模板
+ ### Standard Template
- **所有步骤必须在同一个 `shell_execute` 调用里完成**,避免跨调用的 tab 状态失效。
- 业务逻辑用 `file_write` 写入临时文件再拼接,不用 heredoc(BusyBox ash 遇到花括号/引号容易解析出错)。
+ **All steps must be completed in the same `shell_execute` call** to prevent tab state from becoming invalid across calls.
+ Write business logic to a temporary file with `file_write` and then concatenate it. Do not use a heredoc, because BusyBox ash can easily parse braces or quotes incorrectly.
```bash
- # 前提:用 file_write 把业务逻辑写入 /tmp/asp_logic.js,然后:
+ # Prerequisite: use file_write to write the business logic to /tmp/asp_logic.js, then:
minis-browser-use navigate --url "https://appstoreprice.org/zh/apps" \
&& minis-browser-use wait_for_dom_stable --timeout 8 \
&& minis-browser-use execute_js --script "$(cat /var/minis/skills/appstoreprice-hub/scripts/api.js /tmp/asp_logic.js)"
```
- > **为什么合并到一个 shell_execute?** `minis-browser-use` 的浏览器 tab 在两次 `shell_execute` 之间可能失效(tab 被回收),第二次调用时会报 `webpackChunk_N_E not found`。同一进程内串行执行可保证 tab 状态稳定。
+ > **Why combine everything into one `shell_execute`?** The `minis-browser-use` browser tab may become invalid between two `shell_execute` calls (the tab may be recycled), and the second call may report `webpackChunk_N_E not found`. Serial execution in the same process keeps the tab state stable.
- ## API 速查
+ ## API Quick Reference
- `AppStorePriceAPI()` 返回 `{ search, list, prices, prices_all }`:
+ `AppStorePriceAPI()` returns `{ search, list, prices, prices_all }`:
- | 方法 | 参数 | 返回 |
+ | Method | Parameters | Return |
|---|---|---|
- | `search(query, page=1, limit=20)` | 关键词 | `{ apps, hasMore, total }` |
- | `list(page=1, limit=20)` | 页码/每页数 | `{ apps, hasMore, total }` |
- | `prices(appStoreId, locale='zh')` | App Store ID | **第一个** tier 的价格数组,按 priceUsd 升序 |
- | `prices_all(appStoreId, locale='zh')` | App Store ID | **所有** tier 的价格数组列表(多 tier 订阅必用,如 Claude Pro/Max) |
+ | `search(query, page=1, limit=20)` | Keyword | `{ apps, hasMore, total }` |
+ | `list(page=1, limit=20)` | Page number/items per page | `{ apps, hasMore, total }` |
+ | `prices(appStoreId, locale='zh')` | App Store ID | Price array for the **first** tier, sorted by `priceUsd` in ascending order |
+ | `prices_all(appStoreId, locale='zh')` | App Store ID | List of price arrays for **all** tiers (required for multi-tier subscriptions, such as Claude Pro/Max) |
- `prices` / `prices_all` 每条:`{ region, regionName, currency, price, priceUsd, priceCny }`
+ Each `prices` / `prices_all` item: `{ region, regionName, currency, price, priceUsd, priceCny }`
- > ⚠️ **多 tier 订阅**(如 ChatGPT Plus/Pro、Claude Pro/Max 等)必须用 `prices_all()`,`prices()` 只返回第一个订阅档位。
+ > ⚠️ **Multi-tier subscriptions** (such as ChatGPT Plus/Pro, Claude Pro/Max, etc.) must use `prices_all()`. `prices()` returns only the first subscription tier.
- 常用地区代码:`US` 美国、`TR` 土耳其、`NG` 尼日利亚、`PK` 巴基斯坦、`EG` 埃及、`AR` 阿根廷、`VN` 越南、`JP` 日本、`KR` 韩国、`CN` 中国、`HK` 香港
+ Common region codes: `US` United States, `TR` Turkey, `NG` Nigeria, `PK` Pakistan, `EG` Egypt, `AR` Argentina, `VN` Vietnam, `JP` Japan, `KR` South Korea, `CN` China, `HK` Hong Kong
- ## 典型业务逻辑
+ ## Typical Business Logic
- ### 多 tier 订阅
+ ### Multi-tier Subscriptions
```js
const asp = AppStorePriceAPI();
const sr = await asp.search('Claude');
const app = sr.apps.find(a => a.developer?.includes('Anthropic'));
- const tierNames = ['Claude Pro(月付)', 'Claude Max 5x(月付)', 'Claude Max 20x(月付)', 'Claude Pro(年付)'];
+ const tierNames = ['Claude Pro (monthly)', 'Claude Max 5x (monthly)', 'Claude Max 20x (monthly)', 'Claude Pro (annual)'];
const allTiers = await asp.prices_all(app.appStoreId);
return allTiers.map((prices, i) => {
const sorted = [...prices].sort((a, b) => a.priceUsd - b.priceUsd);
const usPrice = prices.find(p => p.region === 'US')?.priceUsd;
return {
tier: tierNames[i] || `Tier ${i+1}`,
usPriceUsd: usPrice,
cheapestTop5: sorted.slice(0, 5).map(p => ({
...p, saveVsUS: usPrice ? Math.round((1 - p.priceUsd / usPrice) * 100) + '%' : 'N/A'
}))
};
});
```
- ### 最便宜 Top N
+ ### Cheapest Top N
```js
const asp = AppStorePriceAPI();
const sr = await asp.search('ChatGPT');
const app = sr.apps[0];
const all = await asp.prices(app.appStoreId);
const topN = all.sort((a, b) => a.priceUsd - b.priceUsd).slice(0, 10);
const usPrice = all.find(p => p.region === 'US')?.priceUsd;
return { appName: app.name, topN: topN.map(p => ({
...p, saveVsUS: usPrice ? Math.round((1 - p.priceUsd / usPrice) * 100) + '%' : 'N/A'
}))};
```
- ### 指定地区价格
+ ### Price in a Specific Region
```js
const asp = AppStorePriceAPI();
const sr = await asp.search('Notion');
const all = await asp.prices(sr.apps[0].appStoreId);
- return all.find(p => p.region === 'TR'); // 替换地区代码即可
+ return all.find(p => p.region === 'TR'); // Replace the region code as needed.
```
- ## 结果展示规范
+ ## Result Display Guidelines
- 用 Markdown 表格展示,含:地区(国旗 emoji + 名称)、货币、原价、USD 等值、CNY 等值。
- 有对比场景时标注与美区折扣:`节省 = (1 - priceUsd / usPrice) * 100`。
+ Use a Markdown table that includes: region (country flag emoji + name), currency, original price, USD equivalent, and CNY equivalent.
+ In comparison scenarios, indicate the discount versus the U.S. region: `savings = (1 - priceUsd / usPrice) * 100`.
- ## 故障排查
+ ## Troubleshooting
- **签名函数未加载**:确认已 navigate 到 appstoreprice.org 页面并 wait_for_dom_stable。
- `api.js` 会动态扫描所有 webpack module,通过函数体含 `X-Timestamp`/`X-Signature` 字符串来定位签名函数,无需 hardcode module ID。
+ **Signature function not loaded**: Make sure you have navigated to an appstoreprice.org page and called `wait_for_dom_stable`.
+ `api.js` dynamically scans all webpack modules and locates the signing function by checking whether the function body contains the strings `X-Timestamp`/`X-Signature`. There is no need to hardcode the module ID.
- **签名函数未找到(网站大改版)**:若报 "签名函数未找到",说明签名头 key 名称可能已变更。
- 执行以下命令检查新特征:
+ **Signature function not found (major website redesign)**: If you receive "Signature function not found," the signature header key names may have changed.
+ Run the following command to check for new indicators:
```bash
minis-browser-use execute_js --script "
const define=(t,d)=>{for(const k in d) Object.defineProperty(t,k,{get:d[k],enumerable:true})};
const hits=[];
for(const [,m] of self.webpackChunk_N_E){
if(!m) continue;
for(const k of Object.keys(m)){
try{
const e={};m[k]({exports:e},e,{d:define});
for(const fn of Object.values(e)){
if(typeof fn!=='function') continue;
const s=fn.toString();
if(s.includes('X-') && s.length<800) hits.push({module:k,src:s.slice(0,200)});
}
}catch(e){}
}
}
return hits.slice(0,5);
"
```
- 根据输出更新 `api.js` 中 `_getSignFn` 的特征字符串检测条件。
+ Based on the output, update the characteristic string detection conditions in `_getSignFn` in `api.js`.