/ docs / advanced / rate-limiter-plugin.md
rate-limiter-plugin.md
 1  # Rate Limiter Plugin
 2  
 3  An optional plugin that adds a random sleep between browser-based commands to reduce the risk of platform rate-limiting or bot detection.
 4  
 5  ## Install
 6  
 7  ```bash
 8  opencli plugin install github:jackwener/opencli-plugin-rate-limiter
 9  ```
10  
11  Or copy the example below into `~/.opencli/plugins/rate-limiter/` to use it locally without installing from GitHub.
12  
13  ## What it does
14  
15  After every command targeting a browser platform (xiaohongshu, weibo, bilibili, douyin, tiktok, …), the plugin sleeps for a random duration — 5–30 seconds by default — before returning control to the caller.
16  
17  ## Configuration
18  
19  | Variable | Default | Description |
20  |---|---|---|
21  | `OPENCLI_RATE_MIN` | `5` | Minimum sleep in seconds |
22  | `OPENCLI_RATE_MAX` | `30` | Maximum sleep in seconds |
23  | `OPENCLI_NO_RATE` | — | Set to `1` to disable entirely (local dev) |
24  
25  ```bash
26  # Shorter delays for light scraping
27  OPENCLI_RATE_MIN=3 OPENCLI_RATE_MAX=10 opencli xiaohongshu search "AI眼镜"
28  
29  # Skip delays when iterating locally
30  OPENCLI_NO_RATE=1 opencli bilibili comments BV1WtAGzYEBm
31  ```
32  
33  ## Local installation (without GitHub)
34  
35  1. Create the plugin directory:
36  
37     ```bash
38     mkdir -p ~/.opencli/plugins/rate-limiter
39     ```
40  
41  2. Create `~/.opencli/plugins/rate-limiter/package.json`:
42  
43     ```json
44     { "type": "module" }
45     ```
46  
47  3. Create `~/.opencli/plugins/rate-limiter/index.js`:
48  
49     ```js
50     import { onAfterExecute } from '@jackwener/opencli/hooks'
51  
52     const BROWSER_DOMAINS = [
53       'xiaohongshu', 'weibo', 'bilibili', 'douyin', 'tiktok',
54       'instagram', 'twitter', 'youtube', 'zhihu', 'douban',
55       'jike', 'weixin', 'xiaoyuzhou',
56     ]
57  
58     onAfterExecute(async (ctx) => {
59       if (process.env.OPENCLI_NO_RATE === '1') return
60  
61       const site = ctx.command?.split('/')?.[0] ?? ''
62       if (!BROWSER_DOMAINS.includes(site)) return
63  
64       const min = Number(process.env.OPENCLI_RATE_MIN ?? 5)
65       const max = Number(process.env.OPENCLI_RATE_MAX ?? 30)
66       const ms = Math.floor(Math.random() * (max - min + 1) + min) * 1000
67  
68       process.stderr.write(`[rate-limiter] ${site}: sleeping ${(ms / 1000).toFixed(0)}s\n`)
69       await new Promise(r => setTimeout(r, ms))
70     })
71     ```
72  
73  4. Verify it loaded:
74  
75     ```bash
76     OPENCLI_NO_RATE=1 opencli xiaohongshu search "test" 2>&1 | grep rate-limiter
77     # → (no output — plugin loaded but rate limit skipped)
78  
79     opencli xiaohongshu search "test" 2>&1 | grep rate-limiter
80     # → [rate-limiter] xiaohongshu: sleeping 12s
81     ```
82  
83  ## Writing your own plugin
84  
85  Plugins are plain JS/TS files in `~/.opencli/plugins/<name>/`. A plugin file must export a hook registration call that matches the pattern `onStartup(`, `onBeforeExecute(`, or `onAfterExecute(` — opencli's discovery engine uses this pattern to identify hook files vs. command files.
86  
87  ```js
88  // ~/.opencli/plugins/my-plugin/index.js
89  import { onAfterExecute } from '@jackwener/opencli/hooks'
90  
91  onAfterExecute(async (ctx) => {
92    // ctx.command — e.g. "bilibili/comments"
93    // ctx.args    — coerced command arguments
94    // ctx.error   — set if the command threw
95    console.error(`[my-plugin] finished: ${ctx.command}`)
96  })
97  ```
98  
99  See [hooks.ts](../../src/hooks.ts) for the full `HookContext` type.