feat(bilibili): add following adapter — get user's followed accounts

Discovered via Playwright MCP Bridge workflow (CLI-CREATOR.md):
 - Navigate to space.bilibili.com/{uid}/fans/follow
 - Sniff network: GET /x/relation/followings (Cookie-tier, no Wbi needed)
 - Verify API response: total=1342, fields: mid/uname/sign

Also fixed .d.ts loading bug in build-manifest.ts and engine.ts:
 - build-manifest now scans .js files in dist/ (not .ts)
 - engine.ts skips .d.js declaration stubs
This commit is contained in:
jackwener committed 2026-03-15 18:38:34 +08:00
1 parent 53b28637bb
commit f8ee50b776
3 files changed
+55 -2

No files matched your search

+4 -1
View File
@@ -114,7 +114,10 @@ if (fs.existsSync(CLIS_DIR)) {
if (file.endsWith('.yaml') || file.endsWith('.yml')) {
const entry = scanYaml(filePath, site);
if (entry) manifest.push(entry);
} else if (file.endsWith('.ts') && file !== 'index.ts') {
} else if (
(file.endsWith('.ts') && !file.endsWith('.d.ts') && file !== 'index.ts') ||
(file.endsWith('.js') && !file.endsWith('.d.js') && file !== 'index.js')
) {
manifest.push(scanTs(filePath, site));
}
}
+50
View File
@@ -0,0 +1,50 @@
import { cli, Strategy } from '../../registry.js';
import type { IPage } from '../../types.js';
import { fetchJson, getSelfUid, resolveUid } from '../../bilibili.js';
cli({
site: 'bilibili',
name: 'following',
description: '获取 Bilibili 用户的关注列表',
strategy: Strategy.COOKIE,
args: [
{ name: 'uid', required: false, help: '目标用户 ID(默认为当前登录用户)' },
{ name: 'page', type: 'int', required: false, default: 1, help: '页码' },
{ name: 'limit', type: 'int', required: false, default: 50, help: '每页数量 (最大 50)' },
],
columns: ['mid', 'name', 'sign', 'following', 'fans'],
func: async (page: IPage | null, kwargs: any) => {
if (!page) throw new Error('Requires browser');
// 1. Resolve UID (default to self)
const uid = kwargs.uid
? await resolveUid(page, kwargs.uid)
: await getSelfUid(page);
const pn = kwargs.page ?? 1;
const ps = Math.min(kwargs.limit ?? 50, 50);
// 2. Fetch following list (standard Cookie API, no Wbi signing needed)
const payload = await fetchJson(page,
`https://api.bilibili.com/x/relation/followings?vmid=${uid}&pn=${pn}&ps=${ps}&order=desc`
);
if (payload.code !== 0) {
throw new Error(`获取关注列表失败: ${payload.message} (${payload.code})`);
}
const list = payload.data?.list || [];
if (list.length === 0) {
return [{ mid: '-', name: `共 ${payload.data?.total ?? 0} 人关注,当前页无数据`, sign: '', following: '', fans: '' }];
}
// 3. Map to output
return list.map((u: any) => ({
mid: u.mid,
name: u.uname,
sign: (u.sign || '').slice(0, 40),
following: u.attribute === 6 ? '互相关注' : '已关注',
fans: u.official_verify?.desc || '',
}));
},
});
+1 -1
View File
@@ -102,7 +102,7 @@ async function discoverClisFromFs(dir: string): Promise<void> {
const filePath = path.join(siteDir, file);
if (file.endsWith('.yaml') || file.endsWith('.yml')) {
registerYamlCli(filePath, site);
} else if (file.endsWith('.js')) {
} else if (file.endsWith('.js') && !file.endsWith('.d.js')) {
promises.push(
import(`file://${filePath}`).catch((err: any) => {
process.stderr.write(`Warning: failed to load module ${filePath}: ${err.message}\n`);