From c16e61ec4fd4dd35070d144b7a22dba6ffe34d90 Mon Sep 17 00:00:00 2001 From: 0xsline Date: Sun, 22 Mar 2026 01:38:14 +0800 Subject: [PATCH 1/2] feat: add GitHub Trending, Binance, and Weather (Open Meteo) adapters GitHub Trending (2 commands, browser mode): - repos: trending repositories with stars, forks, language filter - developers: trending developers with popular repos Supports --since daily/weekly/monthly and --language filter Binance (11 commands, public API via data-api.binance.vision): - top: top trading pairs by 24h volume - price: single pair 24h price stats - prices: latest prices for all pairs - ticker: 24h ticker statistics - gainers: top gaining pairs by 24h change - losers: top losing pairs by 24h change - trades: recent trades for a pair - depth: order book bid prices - asks: order book ask prices - klines: candlestick/kline data - pairs: list active trading pairs Weather / Open Meteo (11 commands, free public API, no key needed): - current: current weather for a city - forecast: daily forecast up to 16 days - hourly: hourly forecast - search: city geocoding lookup - air: air quality index (simple) - air-quality: detailed air quality (US/EU AQI, PM2.5, PM10, ozone, NO2, SO2) - sunrise: sunrise/sunset times with UV index - wind: detailed wind forecast with gusts and 80m altitude - precipitation: rain/snow forecast with probability - history: historical weather up to 92 past days - compare: side-by-side weather comparison across cities All 24 commands tested with live data. 258 existing tests pass. --- docs/adapters/browser/binance.md | 65 ++++++++++++++++++++ docs/adapters/browser/github-trending.md | 37 +++++++++++ docs/adapters/browser/weather.md | 78 ++++++++++++++++++++++++ src/clis/binance/asks.yaml | 32 ++++++++++ src/clis/binance/depth.yaml | 32 ++++++++++ src/clis/binance/gainers.yaml | 33 ++++++++++ src/clis/binance/klines.yaml | 36 +++++++++++ src/clis/binance/losers.yaml | 32 ++++++++++ src/clis/binance/pairs.yaml | 30 +++++++++ src/clis/binance/price.yaml | 30 +++++++++ src/clis/binance/prices.yaml | 25 ++++++++ src/clis/binance/ticker.yaml | 34 +++++++++++ src/clis/binance/top.yaml | 33 ++++++++++ src/clis/binance/trades.yaml | 32 ++++++++++ src/clis/github-trending/developers.yaml | 50 +++++++++++++++ src/clis/github-trending/repos.yaml | 55 +++++++++++++++++ src/clis/weather/air-quality.yaml | 53 ++++++++++++++++ src/clis/weather/air.yaml | 46 ++++++++++++++ src/clis/weather/compare.yaml | 52 ++++++++++++++++ src/clis/weather/current.yaml | 49 +++++++++++++++ src/clis/weather/forecast.yaml | 52 ++++++++++++++++ src/clis/weather/history.yaml | 51 ++++++++++++++++ src/clis/weather/hourly.yaml | 53 ++++++++++++++++ src/clis/weather/precipitation.yaml | 52 ++++++++++++++++ src/clis/weather/search.yaml | 38 ++++++++++++ src/clis/weather/sunrise.yaml | 50 +++++++++++++++ src/clis/weather/wind.yaml | 53 ++++++++++++++++ 27 files changed, 1183 insertions(+) create mode 100644 docs/adapters/browser/binance.md create mode 100644 docs/adapters/browser/github-trending.md create mode 100644 docs/adapters/browser/weather.md create mode 100644 src/clis/binance/asks.yaml create mode 100644 src/clis/binance/depth.yaml create mode 100644 src/clis/binance/gainers.yaml create mode 100644 src/clis/binance/klines.yaml create mode 100644 src/clis/binance/losers.yaml create mode 100644 src/clis/binance/pairs.yaml create mode 100644 src/clis/binance/price.yaml create mode 100644 src/clis/binance/prices.yaml create mode 100644 src/clis/binance/ticker.yaml create mode 100644 src/clis/binance/top.yaml create mode 100644 src/clis/binance/trades.yaml create mode 100644 src/clis/github-trending/developers.yaml create mode 100644 src/clis/github-trending/repos.yaml create mode 100644 src/clis/weather/air-quality.yaml create mode 100644 src/clis/weather/air.yaml create mode 100644 src/clis/weather/compare.yaml create mode 100644 src/clis/weather/current.yaml create mode 100644 src/clis/weather/forecast.yaml create mode 100644 src/clis/weather/history.yaml create mode 100644 src/clis/weather/hourly.yaml create mode 100644 src/clis/weather/precipitation.yaml create mode 100644 src/clis/weather/search.yaml create mode 100644 src/clis/weather/sunrise.yaml create mode 100644 src/clis/weather/wind.yaml diff --git a/docs/adapters/browser/binance.md b/docs/adapters/browser/binance.md new file mode 100644 index 0000000..6f243f3 --- /dev/null +++ b/docs/adapters/browser/binance.md @@ -0,0 +1,65 @@ +# Binance + +**Mode**: :globe_with_meridians: Public API · **Domain**: `data-api.binance.vision` + +## Commands + +| Command | Description | +|---------|-------------| +| `opencli binance top` | Top trading pairs by 24h volume | +| `opencli binance price` | Quick price check for a trading pair | +| `opencli binance prices` | Latest prices for all trading pairs | +| `opencli binance ticker` | 24h ticker statistics for top pairs by volume | +| `opencli binance gainers` | Top gaining pairs by 24h price change | +| `opencli binance losers` | Top losing pairs by 24h price change | +| `opencli binance trades` | Recent trades for a trading pair | +| `opencli binance depth` | Order book bid prices for a trading pair | +| `opencli binance asks` | Order book ask prices for a trading pair | +| `opencli binance klines` | Candlestick/kline data for a trading pair | +| `opencli binance pairs` | List active trading pairs on Binance | + +## Usage Examples + +```bash +# Top trading pairs by volume +opencli binance top --limit 10 + +# Check price for a specific pair +opencli binance price --symbol BTCUSDT +opencli binance price --symbol ETHUSDT + +# All prices +opencli binance prices --limit 30 + +# 24h ticker stats +opencli binance ticker --limit 10 + +# Top gainers and losers +opencli binance gainers --limit 5 +opencli binance losers --limit 5 + +# Recent trades for BTC +opencli binance trades --symbol BTCUSDT --limit 10 + +# Order book bid and ask prices +opencli binance depth --symbol BTCUSDT --limit 5 +opencli binance asks --symbol BTCUSDT --limit 5 + +# Daily candlestick data +opencli binance klines --symbol BTCUSDT --interval 1d --limit 7 + +# Hourly candles +opencli binance klines --symbol ETHUSDT --interval 1h --limit 24 + +# List trading pairs +opencli binance pairs --limit 10 + +# JSON output +opencli binance top --limit 5 -f json +``` + +## Notes + +- All endpoints use Binance's public data API (no authentication required) +- Symbols are uppercase (e.g., BTCUSDT, ETHUSDT, BNBUSDT) +- Kline intervals: 1m, 5m, 15m, 1h, 4h, 1d, 1w, 1M diff --git a/docs/adapters/browser/github-trending.md b/docs/adapters/browser/github-trending.md new file mode 100644 index 0000000..a33b345 --- /dev/null +++ b/docs/adapters/browser/github-trending.md @@ -0,0 +1,37 @@ +# GitHub Trending + +**Mode**: 🔐 Browser · **Domain**: `github.com` + +## Commands + +| Command | Description | +|---------|-------------| +| `opencli github-trending repos` | Trending repositories | +| `opencli github-trending developers` | Trending developers | + +## Usage Examples + +```bash +# Daily trending repos +opencli github-trending repos --limit 10 + +# Weekly trending +opencli github-trending repos --since weekly + +# Trending Python repos +opencli github-trending repos --language python + +# Trending Rust repos this month +opencli github-trending repos --language rust --since monthly + +# Trending developers +opencli github-trending developers --limit 10 + +# JSON output +opencli github-trending repos --limit 5 -f json +``` + +## Prerequisites + +- Chrome running +- [Browser Bridge extension](/guide/browser-bridge) installed diff --git a/docs/adapters/browser/weather.md b/docs/adapters/browser/weather.md new file mode 100644 index 0000000..6303fe5 --- /dev/null +++ b/docs/adapters/browser/weather.md @@ -0,0 +1,78 @@ +# Weather (Open Meteo) + +**Mode**: Public API + Browser · **Domain**: `api.open-meteo.com` + +Free worldwide weather data powered by [Open Meteo](https://open-meteo.com). No API key required. + +## Commands + +| Command | Description | +|---------|-------------| +| `opencli weather current` | Current weather for a city | +| `opencli weather forecast` | 7-day daily forecast | +| `opencli weather hourly` | Hourly forecast (next 24h) | +| `opencli weather search` | Search for cities (geocoding) | +| `opencli weather air-quality` | Current air quality index | +| `opencli weather sunrise` | Sunrise/sunset times and UV index | +| `opencli weather wind` | Detailed wind forecast | +| `opencli weather precipitation` | Rain and snow forecast | +| `opencli weather compare` | Compare weather across cities | +| `opencli weather history` | Historical weather data | + +## Usage Examples + +```bash +# Current weather +opencli weather current Tokyo +opencli weather current "New York" +opencli weather current London -f json + +# 7-day forecast +opencli weather forecast Paris +opencli weather forecast Berlin --days 14 + +# Hourly forecast +opencli weather hourly Sydney --hours 12 + +# Search for a city (no browser needed) +opencli weather search "San Francisco" +opencli weather search Mumbai --limit 10 + +# Air quality +opencli weather air-quality Beijing +opencli weather air-quality "Los Angeles" + +# Sunrise and sunset times +opencli weather sunrise Tokyo --days 14 + +# Wind forecast +opencli weather wind Chicago --hours 48 + +# Precipitation forecast +opencli weather precipitation London --days 10 + +# Compare multiple cities +opencli weather compare "Tokyo,London,Paris,New York" + +# Historical weather (past 30 days) +opencli weather history Berlin --days 30 + +# JSON output for scripting +opencli weather current Tokyo -f json +opencli weather forecast London -f json +``` + +## Notes + +- The `search` command uses a simple fetch pipeline (no browser needed) +- All other commands use browser mode for the 2-step geocoding lookup (city name -> coordinates -> weather data) +- City names with spaces must be quoted: `"New York"`, `"San Francisco"` +- Temperature is in Celsius, wind in km/h, precipitation in mm +- Air quality uses US AQI scale (0-500) +- Historical data supports up to 92 past days +- Forecast supports up to 16 days ahead + +## Prerequisites + +- Chrome running with [Browser Bridge extension](/guide/browser-bridge) installed +- No API key or login required - Open Meteo is completely free diff --git a/src/clis/binance/asks.yaml b/src/clis/binance/asks.yaml new file mode 100644 index 0000000..cc876cd --- /dev/null +++ b/src/clis/binance/asks.yaml @@ -0,0 +1,32 @@ +site: binance +name: asks +description: Order book ask prices for a trading pair +domain: data-api.binance.vision +strategy: public +browser: false + +args: + symbol: + type: str + required: true + positional: true + description: "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + limit: + type: int + default: 10 + description: Number of price levels (5, 10, 20, 50, 100) + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/depth?symbol=${{ args.symbol }}&limit=${{ args.limit }} + + - select: asks + + - map: + rank: ${{ index + 1 }} + ask_price: ${{ item.0 }} + ask_qty: ${{ item.1 }} + + - limit: ${{ args.limit }} + +columns: [rank, ask_price, ask_qty] diff --git a/src/clis/binance/depth.yaml b/src/clis/binance/depth.yaml new file mode 100644 index 0000000..58ddc68 --- /dev/null +++ b/src/clis/binance/depth.yaml @@ -0,0 +1,32 @@ +site: binance +name: depth +description: Order book bid prices for a trading pair +domain: data-api.binance.vision +strategy: public +browser: false + +args: + symbol: + type: str + required: true + positional: true + description: "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + limit: + type: int + default: 10 + description: Number of price levels (5, 10, 20, 50, 100) + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/depth?symbol=${{ args.symbol }}&limit=${{ args.limit }} + + - select: bids + + - map: + rank: ${{ index + 1 }} + bid_price: ${{ item.0 }} + bid_qty: ${{ item.1 }} + + - limit: ${{ args.limit }} + +columns: [rank, bid_price, bid_qty] diff --git a/src/clis/binance/gainers.yaml b/src/clis/binance/gainers.yaml new file mode 100644 index 0000000..27e5e00 --- /dev/null +++ b/src/clis/binance/gainers.yaml @@ -0,0 +1,33 @@ +site: binance +name: gainers +description: Top gaining trading pairs by 24h price change +domain: data-api.binance.vision +strategy: public +browser: false + +args: + limit: + type: int + default: 10 + description: Number of trading pairs + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/ticker/24hr + + - filter: item.priceChangePercent + + - sort: + by: priceChangePercent + order: desc + + - map: + rank: ${{ index + 1 }} + symbol: ${{ item.symbol }} + price: ${{ item.lastPrice }} + change_24h: ${{ item.priceChangePercent }} + volume: ${{ item.quoteVolume }} + + - limit: ${{ args.limit }} + +columns: [rank, symbol, price, change_24h, volume] diff --git a/src/clis/binance/klines.yaml b/src/clis/binance/klines.yaml new file mode 100644 index 0000000..f2cf3cb --- /dev/null +++ b/src/clis/binance/klines.yaml @@ -0,0 +1,36 @@ +site: binance +name: klines +description: Candlestick/kline data for a trading pair +domain: data-api.binance.vision +strategy: public +browser: false + +args: + symbol: + type: str + required: true + positional: true + description: "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + interval: + type: str + default: 1d + description: "Kline interval (1m, 5m, 15m, 1h, 4h, 1d, 1w, 1M)" + limit: + type: int + default: 10 + description: Number of klines (max 1000) + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/klines?symbol=${{ args.symbol }}&interval=${{ args.interval }}&limit=${{ args.limit }} + + - map: + open: ${{ item.1 }} + high: ${{ item.2 }} + low: ${{ item.3 }} + close: ${{ item.4 }} + volume: ${{ item.5 }} + + - limit: ${{ args.limit }} + +columns: [open, high, low, close, volume] diff --git a/src/clis/binance/losers.yaml b/src/clis/binance/losers.yaml new file mode 100644 index 0000000..a7650bd --- /dev/null +++ b/src/clis/binance/losers.yaml @@ -0,0 +1,32 @@ +site: binance +name: losers +description: Top losing trading pairs by 24h price change +domain: data-api.binance.vision +strategy: public +browser: false + +args: + limit: + type: int + default: 10 + description: Number of trading pairs + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/ticker/24hr + + - filter: item.priceChangePercent + + - sort: + by: priceChangePercent + + - map: + rank: ${{ index + 1 }} + symbol: ${{ item.symbol }} + price: ${{ item.lastPrice }} + change_24h: ${{ item.priceChangePercent }} + volume: ${{ item.quoteVolume }} + + - limit: ${{ args.limit }} + +columns: [rank, symbol, price, change_24h, volume] diff --git a/src/clis/binance/pairs.yaml b/src/clis/binance/pairs.yaml new file mode 100644 index 0000000..d0359b8 --- /dev/null +++ b/src/clis/binance/pairs.yaml @@ -0,0 +1,30 @@ +site: binance +name: pairs +description: List active trading pairs on Binance +domain: data-api.binance.vision +strategy: public +browser: false + +args: + limit: + type: int + default: 20 + description: Number of trading pairs + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/exchangeInfo + + - select: symbols + + - filter: item.status + + - map: + symbol: ${{ item.symbol }} + base: ${{ item.baseAsset }} + quote: ${{ item.quoteAsset }} + status: ${{ item.status }} + + - limit: ${{ args.limit }} + +columns: [symbol, base, quote, status] diff --git a/src/clis/binance/price.yaml b/src/clis/binance/price.yaml new file mode 100644 index 0000000..dd597ba --- /dev/null +++ b/src/clis/binance/price.yaml @@ -0,0 +1,30 @@ +site: binance +name: price +description: Quick price check for a trading pair +domain: data-api.binance.vision +strategy: public +browser: false + +args: + symbol: + type: str + required: true + positional: true + description: "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/ticker/24hr?symbol=${{ args.symbol }} + + - map: + symbol: ${{ item.symbol }} + price: ${{ item.lastPrice }} + change: ${{ item.priceChange }} + change_pct: ${{ item.priceChangePercent }} + high: ${{ item.highPrice }} + low: ${{ item.lowPrice }} + volume: ${{ item.volume }} + quote_volume: ${{ item.quoteVolume }} + trades: ${{ item.count }} + +columns: [symbol, price, change, change_pct, high, low, volume, quote_volume, trades] diff --git a/src/clis/binance/prices.yaml b/src/clis/binance/prices.yaml new file mode 100644 index 0000000..e4553dc --- /dev/null +++ b/src/clis/binance/prices.yaml @@ -0,0 +1,25 @@ +site: binance +name: prices +description: Latest prices for all trading pairs +domain: data-api.binance.vision +strategy: public +browser: false + +args: + limit: + type: int + default: 20 + description: Number of prices + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/ticker/price + + - map: + rank: ${{ index + 1 }} + symbol: ${{ item.symbol }} + price: ${{ item.price }} + + - limit: ${{ args.limit }} + +columns: [rank, symbol, price] diff --git a/src/clis/binance/ticker.yaml b/src/clis/binance/ticker.yaml new file mode 100644 index 0000000..5215b21 --- /dev/null +++ b/src/clis/binance/ticker.yaml @@ -0,0 +1,34 @@ +site: binance +name: ticker +description: 24h ticker statistics for top trading pairs by volume +domain: data-api.binance.vision +strategy: public +browser: false + +args: + limit: + type: int + default: 20 + description: Number of tickers + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/ticker/24hr + + - sort: + by: quoteVolume + order: desc + + - map: + symbol: ${{ item.symbol }} + price: ${{ item.lastPrice }} + change_pct: ${{ item.priceChangePercent }} + high: ${{ item.highPrice }} + low: ${{ item.lowPrice }} + volume: ${{ item.volume }} + quote_vol: ${{ item.quoteVolume }} + trades: ${{ item.count }} + + - limit: ${{ args.limit }} + +columns: [symbol, price, change_pct, high, low, volume, quote_vol, trades] diff --git a/src/clis/binance/top.yaml b/src/clis/binance/top.yaml new file mode 100644 index 0000000..1be8366 --- /dev/null +++ b/src/clis/binance/top.yaml @@ -0,0 +1,33 @@ +site: binance +name: top +description: Top trading pairs by 24h volume on Binance +domain: data-api.binance.vision +strategy: public +browser: false + +args: + limit: + type: int + default: 20 + description: Number of trading pairs + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/ticker/24hr + + - sort: + by: quoteVolume + order: desc + + - map: + rank: ${{ index + 1 }} + symbol: ${{ item.symbol }} + price: ${{ item.lastPrice }} + change_24h: ${{ item.priceChangePercent }} + high: ${{ item.highPrice }} + low: ${{ item.lowPrice }} + volume: ${{ item.quoteVolume }} + + - limit: ${{ args.limit }} + +columns: [rank, symbol, price, change_24h, high, low, volume] diff --git a/src/clis/binance/trades.yaml b/src/clis/binance/trades.yaml new file mode 100644 index 0000000..958e231 --- /dev/null +++ b/src/clis/binance/trades.yaml @@ -0,0 +1,32 @@ +site: binance +name: trades +description: Recent trades for a trading pair +domain: data-api.binance.vision +strategy: public +browser: false + +args: + symbol: + type: str + required: true + positional: true + description: "Trading pair symbol (e.g. BTCUSDT, ETHUSDT)" + limit: + type: int + default: 20 + description: Number of trades (max 1000) + +pipeline: + - fetch: + url: https://data-api.binance.vision/api/v3/trades?symbol=${{ args.symbol }}&limit=${{ args.limit }} + + - map: + id: ${{ item.id }} + price: ${{ item.price }} + qty: ${{ item.qty }} + quote_qty: ${{ item.quoteQty }} + buyer_maker: ${{ item.isBuyerMaker }} + + - limit: ${{ args.limit }} + +columns: [id, price, qty, quote_qty, buyer_maker] diff --git a/src/clis/github-trending/developers.yaml b/src/clis/github-trending/developers.yaml new file mode 100644 index 0000000..6a7b8e8 --- /dev/null +++ b/src/clis/github-trending/developers.yaml @@ -0,0 +1,50 @@ +site: github-trending +name: developers +description: Trending GitHub developers +domain: github.com +browser: true + +args: + since: + type: str + default: daily + description: Time range + choices: [daily, weekly, monthly] + language: + type: str + default: "" + description: "Programming language filter (e.g. python, javascript, rust)" + limit: + type: int + default: 25 + description: Number of developers + +pipeline: + - navigate: + url: https://github.com/trending/developers/${{ args.language }}?since=${{ args.since }} + settleMs: 3000 + + - evaluate: | + (() => { + const limit = ${{ args.limit }}; + const rows = document.querySelectorAll('article.Box-row'); + return Array.from(rows).slice(0, limit).map(function(row, i) { + const nameEl = row.querySelector('h1.h3 a'); + const name = nameEl ? nameEl.textContent.trim() : ''; + const usernameEl = row.querySelector('p.f4 a'); + const username = usernameEl ? usernameEl.textContent.trim() : ''; + const repoEl = row.querySelector('h1.h4 a'); + const repo = repoEl ? repoEl.textContent.trim().replace(/\s+/g, '') : ''; + const repoDesc = row.querySelector('span.repo-snipt-description'); + const desc = repoDesc ? repoDesc.textContent.trim().substring(0, 80) : ''; + return { + rank: i + 1, + name: name || username, + username: username || name, + popular_repo: repo, + description: desc, + }; + }); + })() + +columns: [rank, name, username, popular_repo, description] diff --git a/src/clis/github-trending/repos.yaml b/src/clis/github-trending/repos.yaml new file mode 100644 index 0000000..3803ac7 --- /dev/null +++ b/src/clis/github-trending/repos.yaml @@ -0,0 +1,55 @@ +site: github-trending +name: repos +description: Trending GitHub repositories +domain: github.com +browser: true + +args: + since: + type: str + default: daily + description: Time range + choices: [daily, weekly, monthly] + language: + type: str + default: "" + description: "Programming language filter (e.g. python, javascript, rust, go)" + limit: + type: int + default: 25 + description: Number of repos + +pipeline: + - navigate: + url: https://github.com/trending/${{ args.language }}?since=${{ args.since }} + settleMs: 3000 + + - evaluate: | + (() => { + const limit = ${{ args.limit }}; + const rows = document.querySelectorAll('article.Box-row'); + return Array.from(rows).slice(0, limit).map(function(row, i) { + const nameEl = row.querySelector('h2 a'); + const name = nameEl ? nameEl.textContent.trim().replace(/\s+/g, '') : ''; + const descEl = row.querySelector('p.col-9'); + const desc = descEl ? descEl.textContent.trim().substring(0, 100) : ''; + const langEl = row.querySelector('[itemprop="programmingLanguage"]'); + const lang = langEl ? langEl.textContent.trim() : ''; + const starsEls = row.querySelectorAll('a.Link--muted'); + const stars = starsEls[0] ? starsEls[0].textContent.trim() : ''; + const forks = starsEls[1] ? starsEls[1].textContent.trim() : ''; + const todayEl = row.querySelector('span.d-inline-block.float-sm-right'); + const today = todayEl ? todayEl.textContent.trim() : ''; + return { + rank: i + 1, + repo: name, + description: desc, + language: lang, + stars: stars, + forks: forks, + today: today, + }; + }); + })() + +columns: [rank, repo, language, stars, forks, today, description] diff --git a/src/clis/weather/air-quality.yaml b/src/clis/weather/air-quality.yaml new file mode 100644 index 0000000..82efe53 --- /dev/null +++ b/src/clis/weather/air-quality.yaml @@ -0,0 +1,53 @@ +site: weather +name: air-quality +description: Current air quality index for a city +domain: air-quality-api.open-meteo.com +strategy: public +browser: true + +args: + city: + type: str + required: true + positional: true + description: City name (e.g. Tokyo, London, "New York") + +pipeline: + - navigate: + url: https://open-meteo.com + settleMs: 2000 + + - evaluate: | + (async () => { + const city = ${{ args.city | json }}; + const geoRes = await fetch('https://geocoding-api.open-meteo.com/v1/search?name=' + encodeURIComponent(city) + '&count=1'); + const geoData = await geoRes.json(); + if (!geoData.results || geoData.results.length === 0) throw new Error('City not found: ' + city); + const loc = geoData.results[0]; + const url = 'https://air-quality-api.open-meteo.com/v1/air-quality?latitude=' + loc.latitude + '&longitude=' + loc.longitude + '¤t=us_aqi,european_aqi,pm10,pm2_5,carbon_monoxide,nitrogen_dioxide,sulphur_dioxide,ozone'; + const res = await fetch(url); + const data = await res.json(); + const c = data.current; + const aqi = c.us_aqi; + let level = 'Good'; + if (aqi > 300) level = 'Hazardous'; + else if (aqi > 200) level = 'Very Unhealthy'; + else if (aqi > 150) level = 'Unhealthy'; + else if (aqi > 100) level = 'Unhealthy for Sensitive'; + else if (aqi > 50) level = 'Moderate'; + return [{ + city: loc.name, + country: loc.country_code || '', + us_aqi: c.us_aqi, + eu_aqi: c.european_aqi, + level: level, + pm2_5: c.pm2_5, + pm10: c.pm10, + ozone: c.ozone, + no2: c.nitrogen_dioxide, + so2: c.sulphur_dioxide, + co: c.carbon_monoxide + }]; + })() + +columns: [city, country, us_aqi, eu_aqi, level, pm2_5, pm10, ozone, no2, so2] diff --git a/src/clis/weather/air.yaml b/src/clis/weather/air.yaml new file mode 100644 index 0000000..f52e9ab --- /dev/null +++ b/src/clis/weather/air.yaml @@ -0,0 +1,46 @@ +site: weather +name: air +description: Air quality index for a city +domain: air-quality-api.open-meteo.com +browser: true + +args: + city: + type: str + required: true + positional: true + description: City name + +pipeline: + - navigate: + url: https://open-meteo.com + settleMs: 2000 + + - evaluate: | + (async () => { + const city = ${{ args.city | json }}; + const geoRes = await fetch('https://geocoding-api.open-meteo.com/v1/search?name=' + encodeURIComponent(city) + '&count=1'); + const geo = await geoRes.json(); + if (!geo.results || geo.results.length === 0) throw new Error('City not found: ' + city); + const loc = geo.results[0]; + const res = await fetch('https://air-quality-api.open-meteo.com/v1/air-quality?latitude=' + loc.latitude + '&longitude=' + loc.longitude + '¤t=us_aqi,pm2_5,pm10,carbon_monoxide,nitrogen_dioxide,ozone'); + const d = await res.json(); + const c = d.current; + var level = 'Good'; + if (c.us_aqi > 50) level = 'Moderate'; + if (c.us_aqi > 100) level = 'Unhealthy for Sensitive'; + if (c.us_aqi > 150) level = 'Unhealthy'; + if (c.us_aqi > 200) level = 'Very Unhealthy'; + if (c.us_aqi > 300) level = 'Hazardous'; + return [{ + city: loc.name, + country: loc.country || '', + aqi: c.us_aqi, + level: level, + pm25: c.pm2_5 + ' µg/m³', + pm10: c.pm10 + ' µg/m³', + ozone: c.ozone + ' µg/m³', + }]; + })() + +columns: [city, country, aqi, level, pm25, pm10, ozone] diff --git a/src/clis/weather/compare.yaml b/src/clis/weather/compare.yaml new file mode 100644 index 0000000..873bb04 --- /dev/null +++ b/src/clis/weather/compare.yaml @@ -0,0 +1,52 @@ +site: weather +name: compare +description: Compare current weather across multiple cities +domain: api.open-meteo.com +strategy: public +browser: true + +args: + cities: + type: str + required: true + positional: true + description: "Comma-separated city names (e.g. Tokyo,London,Paris)" + +pipeline: + - navigate: + url: https://open-meteo.com + settleMs: 2000 + + - evaluate: | + (async () => { + const citiesStr = ${{ args.cities | json }}; + const cityNames = citiesStr.split(',').map(s => s.trim()).filter(Boolean); + if (cityNames.length === 0) throw new Error('No cities provided'); + const codes = {0:'Clear',1:'Mainly clear',2:'Partly cloudy',3:'Overcast',45:'Fog',48:'Rime fog',51:'Light drizzle',53:'Drizzle',55:'Dense drizzle',61:'Light rain',63:'Rain',65:'Heavy rain',71:'Light snow',73:'Snow',75:'Heavy snow',80:'Light showers',81:'Showers',82:'Heavy showers',95:'Thunderstorm'}; + const results = []; + for (const name of cityNames) { + const geoRes = await fetch('https://geocoding-api.open-meteo.com/v1/search?name=' + encodeURIComponent(name) + '&count=1'); + const geoData = await geoRes.json(); + if (!geoData.results || geoData.results.length === 0) { + results.push({ city: name, country: '?', condition: 'Not found', temp_c: '-', humidity_pct: '-', wind_kmh: '-', precip_mm: '-' }); + continue; + } + const loc = geoData.results[0]; + const url = 'https://api.open-meteo.com/v1/forecast?latitude=' + loc.latitude + '&longitude=' + loc.longitude + '¤t=temperature_2m,relative_humidity_2m,wind_speed_10m,precipitation,weather_code&timezone=auto'; + const res = await fetch(url); + const data = await res.json(); + const c = data.current; + results.push({ + city: loc.name, + country: loc.country_code || '', + condition: codes[c.weather_code] || ('Code ' + c.weather_code), + temp_c: c.temperature_2m, + humidity_pct: c.relative_humidity_2m, + wind_kmh: c.wind_speed_10m, + precip_mm: c.precipitation + }); + } + return results; + })() + +columns: [city, country, condition, temp_c, humidity_pct, wind_kmh, precip_mm] diff --git a/src/clis/weather/current.yaml b/src/clis/weather/current.yaml new file mode 100644 index 0000000..0a8455b --- /dev/null +++ b/src/clis/weather/current.yaml @@ -0,0 +1,49 @@ +site: weather +name: current +description: Current weather for a city +domain: api.open-meteo.com +strategy: public +browser: true + +args: + city: + type: str + required: true + positional: true + description: City name (e.g. Tokyo, London, "New York") + +pipeline: + - navigate: + url: https://open-meteo.com + settleMs: 2000 + + - evaluate: | + (async () => { + const city = ${{ args.city | json }}; + const geoRes = await fetch('https://geocoding-api.open-meteo.com/v1/search?name=' + encodeURIComponent(city) + '&count=1'); + const geoData = await geoRes.json(); + if (!geoData.results || geoData.results.length === 0) throw new Error('City not found: ' + city); + const loc = geoData.results[0]; + const lat = loc.latitude; + const lon = loc.longitude; + const url = 'https://api.open-meteo.com/v1/forecast?latitude=' + lat + '&longitude=' + lon + '¤t=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_direction_10m,pressure_msl,precipitation,weather_code&timezone=auto'; + const res = await fetch(url); + const data = await res.json(); + const c = data.current; + const codes = {0:'Clear',1:'Mainly clear',2:'Partly cloudy',3:'Overcast',45:'Fog',48:'Rime fog',51:'Light drizzle',53:'Drizzle',55:'Dense drizzle',61:'Light rain',63:'Rain',65:'Heavy rain',66:'Freezing rain',67:'Heavy freezing rain',71:'Light snow',73:'Snow',75:'Heavy snow',77:'Snow grains',80:'Light showers',81:'Showers',82:'Heavy showers',85:'Light snow showers',86:'Heavy snow showers',95:'Thunderstorm',96:'Thunderstorm w/ hail',99:'Heavy thunderstorm w/ hail'}; + return [{ + city: loc.name, + country: loc.country_code || '', + condition: codes[c.weather_code] || ('Code ' + c.weather_code), + temp_c: c.temperature_2m, + feels_like_c: c.apparent_temperature, + humidity_pct: c.relative_humidity_2m, + wind_kmh: c.wind_speed_10m, + wind_dir: c.wind_direction_10m, + pressure_hpa: c.pressure_msl, + precip_mm: c.precipitation, + timezone: data.timezone + }]; + })() + +columns: [city, country, condition, temp_c, feels_like_c, humidity_pct, wind_kmh, pressure_hpa] diff --git a/src/clis/weather/forecast.yaml b/src/clis/weather/forecast.yaml new file mode 100644 index 0000000..ed29642 --- /dev/null +++ b/src/clis/weather/forecast.yaml @@ -0,0 +1,52 @@ +site: weather +name: forecast +description: 7-day weather forecast for a city +domain: api.open-meteo.com +strategy: public +browser: true + +args: + city: + type: str + required: true + positional: true + description: City name (e.g. Tokyo, London, "New York") + days: + type: int + default: 7 + description: Number of forecast days (1-16) + +pipeline: + - navigate: + url: https://open-meteo.com + settleMs: 2000 + + - evaluate: | + (async () => { + const city = ${{ args.city | json }}; + const days = ${{ args.days }}; + const geoRes = await fetch('https://geocoding-api.open-meteo.com/v1/search?name=' + encodeURIComponent(city) + '&count=1'); + const geoData = await geoRes.json(); + if (!geoData.results || geoData.results.length === 0) throw new Error('City not found: ' + city); + const loc = geoData.results[0]; + const url = 'https://api.open-meteo.com/v1/forecast?latitude=' + loc.latitude + '&longitude=' + loc.longitude + '&daily=temperature_2m_max,temperature_2m_min,apparent_temperature_max,apparent_temperature_min,precipitation_sum,precipitation_probability_max,wind_speed_10m_max,weather_code&forecast_days=' + days + '&timezone=auto'; + const res = await fetch(url); + const data = await res.json(); + const d = data.daily; + const codes = {0:'Clear',1:'Mainly clear',2:'Partly cloudy',3:'Overcast',45:'Fog',48:'Rime fog',51:'Light drizzle',53:'Drizzle',55:'Dense drizzle',61:'Light rain',63:'Rain',65:'Heavy rain',71:'Light snow',73:'Snow',75:'Heavy snow',80:'Light showers',81:'Showers',82:'Heavy showers',95:'Thunderstorm',96:'Thunderstorm w/ hail',99:'Heavy thunderstorm w/ hail'}; + const results = []; + for (let i = 0; i < d.time.length; i++) { + results.push({ + date: d.time[i], + condition: codes[d.weather_code[i]] || ('Code ' + d.weather_code[i]), + high_c: d.temperature_2m_max[i], + low_c: d.temperature_2m_min[i], + precip_mm: d.precipitation_sum[i], + rain_pct: d.precipitation_probability_max[i], + wind_kmh: d.wind_speed_10m_max[i] + }); + } + return results; + })() + +columns: [date, condition, high_c, low_c, precip_mm, rain_pct, wind_kmh] diff --git a/src/clis/weather/history.yaml b/src/clis/weather/history.yaml new file mode 100644 index 0000000..db04ed8 --- /dev/null +++ b/src/clis/weather/history.yaml @@ -0,0 +1,51 @@ +site: weather +name: history +description: Historical weather data for a city (past days) +domain: api.open-meteo.com +strategy: public +browser: true + +args: + city: + type: str + required: true + positional: true + description: City name (e.g. Tokyo, London, "New York") + days: + type: int + default: 7 + description: Number of past days (1-92) + +pipeline: + - navigate: + url: https://open-meteo.com + settleMs: 2000 + + - evaluate: | + (async () => { + const city = ${{ args.city | json }}; + const days = Math.min(${{ args.days }}, 92); + const geoRes = await fetch('https://geocoding-api.open-meteo.com/v1/search?name=' + encodeURIComponent(city) + '&count=1'); + const geoData = await geoRes.json(); + if (!geoData.results || geoData.results.length === 0) throw new Error('City not found: ' + city); + const loc = geoData.results[0]; + const url = 'https://api.open-meteo.com/v1/forecast?latitude=' + loc.latitude + '&longitude=' + loc.longitude + '&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,wind_speed_10m_max,weather_code&past_days=' + days + '&forecast_days=0&timezone=auto'; + const res = await fetch(url); + const data = await res.json(); + const d = data.daily; + const codes = {0:'Clear',1:'Mainly clear',2:'Partly cloudy',3:'Overcast',45:'Fog',48:'Rime fog',51:'Light drizzle',53:'Drizzle',55:'Dense drizzle',61:'Light rain',63:'Rain',65:'Heavy rain',71:'Light snow',73:'Snow',75:'Heavy snow',80:'Light showers',81:'Showers',82:'Heavy showers',95:'Thunderstorm'}; + const results = []; + for (let i = 0; i < d.time.length; i++) { + results.push({ + date: d.time[i], + condition: codes[d.weather_code[i]] || ('Code ' + d.weather_code[i]), + high_c: d.temperature_2m_max[i], + low_c: d.temperature_2m_min[i], + precip_mm: d.precipitation_sum[i], + wind_kmh: d.wind_speed_10m_max[i] + }); + } + return results; + })() + +columns: [date, condition, high_c, low_c, precip_mm, wind_kmh] diff --git a/src/clis/weather/hourly.yaml b/src/clis/weather/hourly.yaml new file mode 100644 index 0000000..b1c059d --- /dev/null +++ b/src/clis/weather/hourly.yaml @@ -0,0 +1,53 @@ +site: weather +name: hourly +description: Hourly weather forecast for a city (next 24h) +domain: api.open-meteo.com +strategy: public +browser: true + +args: + city: + type: str + required: true + positional: true + description: City name (e.g. Tokyo, London, "New York") + hours: + type: int + default: 24 + description: Number of hours to show + +pipeline: + - navigate: + url: https://open-meteo.com + settleMs: 2000 + + - evaluate: | + (async () => { + const city = ${{ args.city | json }}; + const hours = ${{ args.hours }}; + const geoRes = await fetch('https://geocoding-api.open-meteo.com/v1/search?name=' + encodeURIComponent(city) + '&count=1'); + const geoData = await geoRes.json(); + if (!geoData.results || geoData.results.length === 0) throw new Error('City not found: ' + city); + const loc = geoData.results[0]; + const url = 'https://api.open-meteo.com/v1/forecast?latitude=' + loc.latitude + '&longitude=' + loc.longitude + '&hourly=temperature_2m,apparent_temperature,precipitation_probability,precipitation,weather_code,wind_speed_10m,relative_humidity_2m&forecast_hours=' + hours + '&timezone=auto'; + const res = await fetch(url); + const data = await res.json(); + const h = data.hourly; + const codes = {0:'Clear',1:'Mainly clear',2:'Partly cloudy',3:'Overcast',45:'Fog',48:'Rime fog',51:'Light drizzle',53:'Drizzle',55:'Dense drizzle',61:'Light rain',63:'Rain',65:'Heavy rain',71:'Light snow',73:'Snow',75:'Heavy snow',80:'Light showers',81:'Showers',82:'Heavy showers',95:'Thunderstorm'}; + const results = []; + for (let i = 0; i < h.time.length && i < hours; i++) { + results.push({ + time: h.time[i].replace('T', ' '), + condition: codes[h.weather_code[i]] || ('Code ' + h.weather_code[i]), + temp_c: h.temperature_2m[i], + feels_c: h.apparent_temperature[i], + humidity_pct: h.relative_humidity_2m[i], + rain_pct: h.precipitation_probability[i], + precip_mm: h.precipitation[i], + wind_kmh: h.wind_speed_10m[i] + }); + } + return results; + })() + +columns: [time, condition, temp_c, feels_c, humidity_pct, rain_pct, wind_kmh] diff --git a/src/clis/weather/precipitation.yaml b/src/clis/weather/precipitation.yaml new file mode 100644 index 0000000..af87849 --- /dev/null +++ b/src/clis/weather/precipitation.yaml @@ -0,0 +1,52 @@ +site: weather +name: precipitation +description: Precipitation forecast for a city +domain: api.open-meteo.com +strategy: public +browser: true + +args: + city: + type: str + required: true + positional: true + description: City name (e.g. Tokyo, London, "New York") + days: + type: int + default: 7 + description: Number of forecast days + +pipeline: + - navigate: + url: https://open-meteo.com + settleMs: 2000 + + - evaluate: | + (async () => { + const city = ${{ args.city | json }}; + const days = ${{ args.days }}; + const geoRes = await fetch('https://geocoding-api.open-meteo.com/v1/search?name=' + encodeURIComponent(city) + '&count=1'); + const geoData = await geoRes.json(); + if (!geoData.results || geoData.results.length === 0) throw new Error('City not found: ' + city); + const loc = geoData.results[0]; + const url = 'https://api.open-meteo.com/v1/forecast?latitude=' + loc.latitude + '&longitude=' + loc.longitude + '&daily=precipitation_sum,rain_sum,snowfall_sum,precipitation_hours,precipitation_probability_max,weather_code&forecast_days=' + days + '&timezone=auto'; + const res = await fetch(url); + const data = await res.json(); + const d = data.daily; + const codes = {0:'Clear',1:'Mainly clear',2:'Partly cloudy',3:'Overcast',45:'Fog',48:'Rime fog',51:'Light drizzle',53:'Drizzle',55:'Dense drizzle',61:'Light rain',63:'Rain',65:'Heavy rain',71:'Light snow',73:'Snow',75:'Heavy snow',80:'Light showers',81:'Showers',82:'Heavy showers',95:'Thunderstorm'}; + const results = []; + for (let i = 0; i < d.time.length; i++) { + results.push({ + date: d.time[i], + condition: codes[d.weather_code[i]] || ('Code ' + d.weather_code[i]), + total_mm: d.precipitation_sum[i], + rain_mm: d.rain_sum[i], + snow_cm: d.snowfall_sum[i], + hours: d.precipitation_hours[i], + probability: d.precipitation_probability_max[i] + }); + } + return results; + })() + +columns: [date, condition, total_mm, rain_mm, snow_cm, hours, probability] diff --git a/src/clis/weather/search.yaml b/src/clis/weather/search.yaml new file mode 100644 index 0000000..ace215a --- /dev/null +++ b/src/clis/weather/search.yaml @@ -0,0 +1,38 @@ +site: weather +name: search +description: Search for cities (geocoding) +domain: geocoding-api.open-meteo.com +strategy: public +browser: false + +args: + query: + type: str + required: true + positional: true + description: City name to search + limit: + type: int + default: 5 + description: Number of results + +pipeline: + - fetch: + url: https://geocoding-api.open-meteo.com/v1/search?name=${{ args.query }}&count=${{ args.limit }} + + - select: results + + - map: + rank: ${{ index + 1 }} + name: ${{ item.name }} + country: ${{ item.country }} + admin: ${{ item.admin1 }} + latitude: ${{ item.latitude }} + longitude: ${{ item.longitude }} + elevation: ${{ item.elevation }} + timezone: ${{ item.timezone }} + population: ${{ item.population }} + + - limit: ${{ args.limit }} + +columns: [rank, name, country, admin, latitude, longitude, elevation, timezone, population] diff --git a/src/clis/weather/sunrise.yaml b/src/clis/weather/sunrise.yaml new file mode 100644 index 0000000..3c5e197 --- /dev/null +++ b/src/clis/weather/sunrise.yaml @@ -0,0 +1,50 @@ +site: weather +name: sunrise +description: Sunrise and sunset times for a city +domain: api.open-meteo.com +strategy: public +browser: true + +args: + city: + type: str + required: true + positional: true + description: City name (e.g. Tokyo, London, "New York") + days: + type: int + default: 7 + description: Number of days to show + +pipeline: + - navigate: + url: https://open-meteo.com + settleMs: 2000 + + - evaluate: | + (async () => { + const city = ${{ args.city | json }}; + const days = ${{ args.days }}; + const geoRes = await fetch('https://geocoding-api.open-meteo.com/v1/search?name=' + encodeURIComponent(city) + '&count=1'); + const geoData = await geoRes.json(); + if (!geoData.results || geoData.results.length === 0) throw new Error('City not found: ' + city); + const loc = geoData.results[0]; + const url = 'https://api.open-meteo.com/v1/forecast?latitude=' + loc.latitude + '&longitude=' + loc.longitude + '&daily=sunrise,sunset,daylight_duration,uv_index_max&forecast_days=' + days + '&timezone=auto'; + const res = await fetch(url); + const data = await res.json(); + const d = data.daily; + const results = []; + for (let i = 0; i < d.time.length; i++) { + const dlHrs = (d.daylight_duration[i] / 3600).toFixed(1); + results.push({ + date: d.time[i], + sunrise: d.sunrise[i] ? d.sunrise[i].replace('T', ' ') : '-', + sunset: d.sunset[i] ? d.sunset[i].replace('T', ' ') : '-', + daylight_hrs: dlHrs, + uv_index: d.uv_index_max[i] + }); + } + return results; + })() + +columns: [date, sunrise, sunset, daylight_hrs, uv_index] diff --git a/src/clis/weather/wind.yaml b/src/clis/weather/wind.yaml new file mode 100644 index 0000000..5e7a30d --- /dev/null +++ b/src/clis/weather/wind.yaml @@ -0,0 +1,53 @@ +site: weather +name: wind +description: Wind forecast for a city +domain: api.open-meteo.com +strategy: public +browser: true + +args: + city: + type: str + required: true + positional: true + description: City name (e.g. Tokyo, London, "New York") + hours: + type: int + default: 24 + description: Number of hours to show + +pipeline: + - navigate: + url: https://open-meteo.com + settleMs: 2000 + + - evaluate: | + (async () => { + const city = ${{ args.city | json }}; + const hours = ${{ args.hours }}; + const geoRes = await fetch('https://geocoding-api.open-meteo.com/v1/search?name=' + encodeURIComponent(city) + '&count=1'); + const geoData = await geoRes.json(); + if (!geoData.results || geoData.results.length === 0) throw new Error('City not found: ' + city); + const loc = geoData.results[0]; + const url = 'https://api.open-meteo.com/v1/forecast?latitude=' + loc.latitude + '&longitude=' + loc.longitude + '&hourly=wind_speed_10m,wind_direction_10m,wind_gusts_10m,wind_speed_80m,wind_direction_80m&forecast_hours=' + hours + '&timezone=auto'; + const res = await fetch(url); + const data = await res.json(); + const h = data.hourly; + const dirs = ['N','NNE','NE','ENE','E','ESE','SE','SSE','S','SSW','SW','WSW','W','WNW','NW','NNW']; + function degToDir(deg) { return dirs[Math.round(deg / 22.5) % 16]; } + const results = []; + for (let i = 0; i < h.time.length && i < hours; i++) { + results.push({ + time: h.time[i].replace('T', ' '), + speed_kmh: h.wind_speed_10m[i], + direction: degToDir(h.wind_direction_10m[i]), + deg: h.wind_direction_10m[i], + gusts_kmh: h.wind_gusts_10m[i], + speed_80m: h.wind_speed_80m[i], + dir_80m: degToDir(h.wind_direction_80m[i]) + }); + } + return results; + })() + +columns: [time, speed_kmh, direction, gusts_kmh, speed_80m, dir_80m] From 2eee2e5fa8279f35322a9088cac87ce5c66335e5 Mon Sep 17 00:00:00 2001 From: 0xsline Date: Sun, 22 Mar 2026 01:40:49 +0800 Subject: [PATCH 2/2] docs: add missing douban, sinablog, substack adapter documentation --- docs/adapters/browser/douban.md | 23 +++++++++++++++++++++++ docs/adapters/browser/sinablog.md | 24 ++++++++++++++++++++++++ docs/adapters/browser/substack.md | 23 +++++++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 docs/adapters/browser/douban.md create mode 100644 docs/adapters/browser/sinablog.md create mode 100644 docs/adapters/browser/substack.md diff --git a/docs/adapters/browser/douban.md b/docs/adapters/browser/douban.md new file mode 100644 index 0000000..d310ff6 --- /dev/null +++ b/docs/adapters/browser/douban.md @@ -0,0 +1,23 @@ +# Douban + +**Mode**: 🌐 Public · **Domain**: `douban.com` + +## Commands + +| Command | Description | +|---------|-------------| +| `opencli douban movie-hot` | Hot movies on Douban | +| `opencli douban book-hot` | Hot books on Douban | +| `opencli douban search` | Search Douban | + +## Usage Examples + +```bash +opencli douban movie-hot --limit 10 +opencli douban book-hot --limit 10 +opencli douban search --query "三体" +``` + +## Prerequisites + +None — public data, no login required. diff --git a/docs/adapters/browser/sinablog.md b/docs/adapters/browser/sinablog.md new file mode 100644 index 0000000..52b4458 --- /dev/null +++ b/docs/adapters/browser/sinablog.md @@ -0,0 +1,24 @@ +# Sina Blog + +**Mode**: 🌐 Public · **Domain**: `blog.sina.com.cn` + +## Commands + +| Command | Description | +|---------|-------------| +| `opencli sinablog hot` | Hot blog posts | +| `opencli sinablog search` | Search blog posts | +| `opencli sinablog article` | Read a blog article | +| `opencli sinablog user` | User's blog posts | + +## Usage Examples + +```bash +opencli sinablog hot --limit 10 +opencli sinablog search --query "科技" +opencli sinablog user --username someone +``` + +## Prerequisites + +None — public data, no login required. diff --git a/docs/adapters/browser/substack.md b/docs/adapters/browser/substack.md new file mode 100644 index 0000000..6573b22 --- /dev/null +++ b/docs/adapters/browser/substack.md @@ -0,0 +1,23 @@ +# Substack + +**Mode**: 🌐 Public · **Domain**: `substack.com` + +## Commands + +| Command | Description | +|---------|-------------| +| `opencli substack search` | Search Substack publications | +| `opencli substack feed` | Publication feed | +| `opencli substack publication` | Publication info | + +## Usage Examples + +```bash +opencli substack search --query "AI" +opencli substack feed --name "platformer" +opencli substack publication --name "platformer" +``` + +## Prerequisites + +None — public data, no login required.