fix: normalize list privacy parsing

This commit is contained in:
Peter Steinberger
2026-01-03 16:55:29 +01:00
parent 0e8a1349c5
commit e537e102e3
4 changed files with 52 additions and 3 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
### Added ### Added
- Bookmark exports now support pagination (`--all`, `--max-pages`) with retries (#15) — thanks @Nano1337. - Bookmark exports now support pagination (`--all`, `--max-pages`) with retries (#15) — thanks @Nano1337.
- `lists` + `list-timeline` commands for Twitter Lists (#21) — thanks @harperreed
## 0.5.1 — 2026-01-01 ## 0.5.1 — 2026-01-01
### Changed ### Changed
+1 -1
View File
@@ -84,7 +84,7 @@ export function createProgram(ctx: CliContext): Command {
].join('\n\n')}\n\n${ctx.colors.section('Shortcuts')}\n${[ ].join('\n\n')}\n\n${ctx.colors.section('Shortcuts')}\n${[
formatExample('bird <tweet-id-or-url> [--json]', 'Shorthand for `bird read <tweet-id-or-url>`'), formatExample('bird <tweet-id-or-url> [--json]', 'Shorthand for `bird read <tweet-id-or-url>`'),
].join('\n\n')}\n\n${ctx.colors.section('JSON Output')}\n${ctx.colors.muted( ].join('\n\n')}\n\n${ctx.colors.section('JSON Output')}\n${ctx.colors.muted(
` Add ${ctx.colors.option('--json')} to: read, replies, thread, search, mentions, bookmarks, likes, following, followers, query-ids`, ` Add ${ctx.colors.option('--json')} to: read, replies, thread, search, mentions, bookmarks, likes, following, followers, lists, list-timeline, query-ids`,
)}\n${ctx.colors.muted( )}\n${ctx.colors.muted(
` Add ${ctx.colors.option('--json-full')} to include raw API response in ${ctx.colors.argument('_raw')} field (tweet commands only)`, ` Add ${ctx.colors.option('--json-full')} to include raw API response in ${ctx.colors.argument('_raw')} field (tweet commands only)`,
)}\n${ctx.colors.muted(` (Run ${ctx.colors.command('bird <command> --help')} to see per-command flags.)`)}`, )}\n${ctx.colors.muted(` (Run ${ctx.colors.command('bird <command> --help')} to see per-command flags.)`)}`,
+1 -1
View File
@@ -45,7 +45,7 @@ function parseList(listResult: GraphqlListResult): TwitterList | null {
description: listResult.description, description: listResult.description,
memberCount: listResult.member_count, memberCount: listResult.member_count,
subscriberCount: listResult.subscriber_count, subscriberCount: listResult.subscriber_count,
isPrivate: listResult.mode === 'Private', isPrivate: listResult.mode?.toLowerCase() === 'private',
createdAt: listResult.created_at, createdAt: listResult.created_at,
owner: owner owner: owner
? { ? {
+49
View File
@@ -136,6 +136,55 @@ describe('TwitterClient lists', () => {
expect(result.lists?.[0].isPrivate).toBe(true); expect(result.lists?.[0].isPrivate).toBe(true);
}); });
it('handles lowercase private mode', async () => {
mockFetch.mockResolvedValueOnce({
ok: true,
status: 200,
json: async () => ({
data: {
user: {
result: {
timeline: {
timeline: {
instructions: [
{
entries: [
{
content: {
itemContent: {
list: {
id_str: '8888',
name: 'Lowercase Private',
mode: 'private',
},
},
},
},
],
},
],
},
},
},
},
},
}),
});
const client = new TwitterClient({ cookies: validCookies });
const clientPrivate = client as unknown as TwitterClientPrivate;
clientPrivate.getCurrentUser = async () => ({
success: true,
user: { id: '12345', username: 'testuser', name: 'Test User' },
});
clientPrivate.getListOwnershipsQueryIds = async () => ['test'];
const result = await client.getOwnedLists(100);
expect(result.success).toBe(true);
expect(result.lists?.[0].isPrivate).toBe(true);
});
it('returns empty array when no lists exist', async () => { it('returns empty array when no lists exist', async () => {
mockFetch.mockResolvedValueOnce({ mockFetch.mockResolvedValueOnce({
ok: true, ok: true,