diff --git a/src/build-manifest.ts b/src/build-manifest.ts index 121291f..3b2e565 100644 --- a/src/build-manifest.ts +++ b/src/build-manifest.ts @@ -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)); } } diff --git a/src/clis/bilibili/following.ts b/src/clis/bilibili/following.ts new file mode 100644 index 0000000..a8840fd --- /dev/null +++ b/src/clis/bilibili/following.ts @@ -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 || '', + })); + }, +}); diff --git a/src/engine.ts b/src/engine.ts index 278ec99..719072a 100644 --- a/src/engine.ts +++ b/src/engine.ts @@ -102,7 +102,7 @@ async function discoverClisFromFs(dir: string): Promise { 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`);