|
| 1 | +# Copilot Instructions |
| 2 | + |
| 3 | +## Commands |
| 4 | + |
| 5 | +```bash |
| 6 | +pnpm start # Run all bots (node ./src/index.js) |
| 7 | +node scripts/setup-bots.js # One-time: register all bot accounts against the API |
| 8 | +``` |
| 9 | + |
| 10 | +Formatting is enforced automatically on commit via Husky + lint-staged (Prettier). To format manually: |
| 11 | + |
| 12 | +```bash |
| 13 | +pnpm exec prettier --write . |
| 14 | +``` |
| 15 | + |
| 16 | +There is no test suite. |
| 17 | + |
| 18 | +## Architecture |
| 19 | + |
| 20 | +This is a Node.js ESM project (`"type": "module"`) that runs ~80 tech-topic bots. Each bot monitors either a GitHub releases feed, an RSS feed, or both, and publishes posts to a TDN social platform via a REST API. |
| 21 | + |
| 22 | +### Layer overview |
| 23 | + |
| 24 | +``` |
| 25 | +src/ |
| 26 | + index.js — Entry point. Imports every bot module and starts them all. |
| 27 | + logger.js — Shared timestamped, color-coded console logger. |
| 28 | + api/ |
| 29 | + client.js — TdnClient: per-bot HTTP client with login, token refresh, createPost(). |
| 30 | + core/ |
| 31 | + BaseUpdateWatcher.js — EventEmitter that polls GitHub releases API; emits 'new_update'. |
| 32 | + BaseNewsWatcher.js — EventEmitter that polls RSS via rss2json; emits 'new_article'. |
| 33 | + requestScheduler.js — Centralized rate-limited fetch queue (separate lanes: 'github', 'rss'). |
| 34 | + utils.js — buildNewsPost(), buildUpdatePost(), HTML/Markdown strippers. |
| 35 | + bots/ |
| 36 | + {name}/ |
| 37 | + update.js — Concrete update bot: wires BaseUpdateWatcher → TdnClient.createPost(). |
| 38 | + news.js — Concrete news bot: wires BaseNewsWatcher → TdnClient.createPost(). |
| 39 | +scripts/ |
| 40 | + setup-bots.js — Registers, logs in, and sets profile/avatar/banner for each bot account. |
| 41 | +config.json — Bot credentials + API base URL (gitignored; must be created locally). |
| 42 | +``` |
| 43 | + |
| 44 | +### Data flow |
| 45 | + |
| 46 | +1. `BaseUpdateWatcher` / `BaseNewsWatcher` polls its API on an interval with jitter. |
| 47 | +2. On a new result it emits `new_update` or `new_article`. |
| 48 | +3. The bot module handles the event, calls `buildUpdatePost()` / `buildNewsPost()`, and calls `client.createPost()`. |
| 49 | +4. `TdnClient` sends the post to `POST /posts` with Bearer auth, auto-refreshing the token on 401. |
| 50 | +5. State (last seen tag or article link) is persisted as `updates_state.json` / `news_state.json` **inside the bot's own directory** (`__dirname`-relative). |
| 51 | + |
| 52 | +### Rate limiting |
| 53 | + |
| 54 | +`requestScheduler.js` routes all outbound fetches through two lanes: |
| 55 | + |
| 56 | +- `github`: max 2 concurrent, 600 ms minimum between requests |
| 57 | +- `rss`: max 1 concurrent, 5 s minimum between requests (rss2json free tier) |
| 58 | + |
| 59 | +Both watchers implement exponential backoff (initial 30 s, max 1 h) on errors or 429/403 responses. Startup jitter spreads initial requests over 1–2 minutes to avoid thundering-herd bursts. |
| 60 | + |
| 61 | +## Key Conventions |
| 62 | + |
| 63 | +### Adding a new bot |
| 64 | + |
| 65 | +1. Add the bot entry to `config.json` under `bots` with `username`, `email`, `password`. |
| 66 | +2. Create `src/bots/{name}/update.js` and/or `src/bots/{name}/news.js` using an existing bot as a template (e.g., `src/bots/typescript/`). |
| 67 | + - Export a single `start{Name}{Type}Bot()` function. |
| 68 | + - Pass `stateDir: __dirname` to the watcher so state files land next to the module. |
| 69 | + - Use `createTdnClient("{name}")` where `{name}` matches the key in `config.json`. |
| 70 | +3. Import and call the start function in `src/index.js`. |
| 71 | +4. Run `node scripts/setup-bots.js` to provision the new account on the platform. |
| 72 | + |
| 73 | +### Post payload shape |
| 74 | + |
| 75 | +```js |
| 76 | +{ content: string, type: "TECH_NEWS" | "SYSTEM_UPDATE", mediaUrls: string[] } |
| 77 | +``` |
| 78 | + |
| 79 | +Posts are hard-capped at **300 characters** (enforced in `utils.js`). News posts may include a thumbnail in `mediaUrls`; update posts always use an empty array. |
| 80 | + |
| 81 | +### Environment |
| 82 | + |
| 83 | +- `config.json` is gitignored. Bot credentials and `apiBaseUrl` live there. |
| 84 | +- Optional `GITHUB_TOKEN` env var (in `.env`): used by `BaseUpdateWatcher` to raise the GitHub API rate limit. |
| 85 | + |
| 86 | +### Code style (Prettier) |
| 87 | + |
| 88 | +4-space indent, double quotes, semicolons, trailing commas (`"trailingComma": "all"`), 80-char print width. |
0 commit comments