fix: adapt upstream commands to relay client

This commit is contained in:
2026-06-24 18:51:33 +09:00
parent 55fd5c3d81
commit 66d04f3a92
8 changed files with 17 additions and 104 deletions
+6 -26
View File
@@ -42,19 +42,9 @@ export function registerFollowCommands(program: Command, ctx: CliContext): void
.action(async (usernameOrId: string) => { .action(async (usernameOrId: string) => {
const opts = program.opts(); const opts = program.opts();
const timeoutMs = ctx.resolveTimeoutFromOptions(opts); const timeoutMs = ctx.resolveTimeoutFromOptions(opts);
const relayBaseUrl = ctx.resolveRelayBaseUrlFromOptions(opts);
const { cookies, warnings } = await ctx.resolveCredentialsFromOptions(opts); const profileName = ctx.resolveProfileNameFromOptions(opts);
const client = new TwitterClient({ relayBaseUrl, profileName, timeoutMs });
for (const warning of warnings) {
console.error(`${ctx.p('warn')}${warning}`);
}
if (!cookies.authToken || !cookies.ct0) {
console.error(`${ctx.p('err')}Missing required credentials`);
process.exit(1);
}
const client = new TwitterClient({ cookies, timeoutMs });
const resolved = await resolveUserId(client, usernameOrId, ctx); const resolved = await resolveUserId(client, usernameOrId, ctx);
if (!resolved) { if (!resolved) {
@@ -81,19 +71,9 @@ export function registerFollowCommands(program: Command, ctx: CliContext): void
.action(async (usernameOrId: string) => { .action(async (usernameOrId: string) => {
const opts = program.opts(); const opts = program.opts();
const timeoutMs = ctx.resolveTimeoutFromOptions(opts); const timeoutMs = ctx.resolveTimeoutFromOptions(opts);
const relayBaseUrl = ctx.resolveRelayBaseUrlFromOptions(opts);
const { cookies, warnings } = await ctx.resolveCredentialsFromOptions(opts); const profileName = ctx.resolveProfileNameFromOptions(opts);
const client = new TwitterClient({ relayBaseUrl, profileName, timeoutMs });
for (const warning of warnings) {
console.error(`${ctx.p('warn')}${warning}`);
}
if (!cookies.authToken || !cookies.ct0) {
console.error(`${ctx.p('err')}Missing required credentials`);
process.exit(1);
}
const client = new TwitterClient({ cookies, timeoutMs });
const resolved = await resolveUserId(client, usernameOrId, ctx); const resolved = await resolveUserId(client, usernameOrId, ctx);
if (!resolved) { if (!resolved) {
+3 -63
View File
@@ -317,18 +317,9 @@ export function registerUserCommands(program: Command, ctx: CliContext): void {
process.exit(1); process.exit(1);
} }
const { cookies, warnings } = await ctx.resolveCredentialsFromOptions(opts); const relayBaseUrl = ctx.resolveRelayBaseUrlFromOptions(opts);
const profileName = ctx.resolveProfileNameFromOptions(opts);
for (const warning of warnings) { const client = new TwitterClient({ relayBaseUrl, profileName, timeoutMs });
console.error(`${ctx.p('warn')}${warning}`);
}
if (!cookies.authToken || !cookies.ct0) {
console.error(`${ctx.p('err')}Missing required credentials`);
process.exit(1);
}
const client = new TwitterClient({ cookies, timeoutMs });
const result = await client.getUserAboutAccount(normalizedHandle); const result = await client.getUserAboutAccount(normalizedHandle);
if (result.success && result.aboutProfile) { if (result.success && result.aboutProfile) {
@@ -344,55 +335,4 @@ export function registerUserCommands(program: Command, ctx: CliContext): void {
process.exit(1); process.exit(1);
} }
}); });
program
.command('about')
.description('Get account origin and location information for a user')
.argument('<username>', 'Twitter username (with or without @)')
.option('--json', 'Output as JSON')
.action(async (username: string, cmdOpts: { json?: boolean }) => {
const opts = program.opts();
const timeoutMs = ctx.resolveTimeoutFromOptions(opts);
const { cookies, warnings } = await ctx.resolveCredentialsFromOptions(opts);
for (const warning of warnings) {
console.error(`${ctx.p('warn')}${warning}`);
}
if (!cookies.authToken || !cookies.ct0) {
console.error(`${ctx.p('err')}Missing required credentials`);
process.exit(1);
}
const client = new TwitterClient({ cookies, timeoutMs });
const result = await client.getUserAboutAccount(username);
if (result.success && result.aboutProfile) {
if (cmdOpts.json) {
console.log(JSON.stringify(result.aboutProfile, null, 2));
} else {
const profile = result.aboutProfile;
console.log(`${ctx.p('info')}Account information for @${username}:`);
if (profile.accountBasedIn) {
console.log(` Account based in: ${profile.accountBasedIn}`);
}
if (profile.createdCountryAccurate !== undefined) {
console.log(` Creation country accurate: ${profile.createdCountryAccurate ? 'Yes' : 'No'}`);
}
if (profile.locationAccurate !== undefined) {
console.log(` Location accurate: ${profile.locationAccurate ? 'Yes' : 'No'}`);
}
if (profile.source) {
console.log(`${ctx.l('source')}${profile.source}`);
}
if (profile.learnMoreUrl) {
console.log(` Learn more: ${profile.learnMoreUrl}`);
}
}
} else {
console.error(`${ctx.p('err')}Failed to fetch account information: ${result.error ?? 'Unknown error'}`);
process.exit(1);
}
});
} }
+1 -1
View File
@@ -66,7 +66,7 @@ export function withFollow<TBase extends AbstractConstructor<TwitterClientBase>>
const response = await this.fetchWithTimeout(url, { const response = await this.fetchWithTimeout(url, {
method: 'POST', method: 'POST',
headers: { headers: {
...this.getBaseHeaders(), ...this.getHeaders(),
'content-type': 'application/x-www-form-urlencoded', 'content-type': 'application/x-www-form-urlencoded',
}, },
body: params.toString(), body: params.toString(),
+2 -4
View File
@@ -6,10 +6,8 @@ import { TwitterClient } from '../src/lib/twitter-client.js';
const baseCtx = { const baseCtx = {
resolveTimeoutFromOptions: () => undefined, resolveTimeoutFromOptions: () => undefined,
resolveCredentialsFromOptions: async () => ({ resolveRelayBaseUrlFromOptions: () => 'http://relay.test',
cookies: { authToken: 'auth', ct0: 'ct0', cookieHeader: 'auth=auth; ct0=ct0' }, resolveProfileNameFromOptions: () => undefined,
warnings: [],
}),
p: () => '', p: () => '',
} as unknown as CliContext; } as unknown as CliContext;
+2 -4
View File
@@ -6,10 +6,8 @@ import { TwitterClient } from '../src/lib/twitter-client.js';
const baseCtx = { const baseCtx = {
resolveTimeoutFromOptions: () => undefined, resolveTimeoutFromOptions: () => undefined,
resolveCredentialsFromOptions: async () => ({ resolveRelayBaseUrlFromOptions: () => 'http://relay.test',
cookies: { authToken: 'auth', ct0: 'ct0', cookieHeader: 'auth=auth; ct0=ct0' }, resolveProfileNameFromOptions: () => undefined,
warnings: [],
}),
p: () => '', p: () => '',
l: () => '', l: () => '',
printTweets: () => undefined, printTweets: () => undefined,
-2
View File
@@ -2,8 +2,6 @@ import { spawn } from 'node:child_process';
import { existsSync } from 'node:fs'; import { existsSync } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { beforeAll, describe, expect, it } from 'vitest'; import { beforeAll, describe, expect, it } from 'vitest';
import { resolveCredentials } from '../../src/lib/cookies.js';
import { TwitterClient } from '../../src/lib/twitter-client.js';
type RunResult = { exitCode: number; stdout: string; stderr: string; signal: NodeJS.Signals | null }; type RunResult = { exitCode: number; stdout: string; stderr: string; signal: NodeJS.Signals | null };
-2
View File
@@ -2,8 +2,6 @@ import { spawn } from 'node:child_process';
import { existsSync } from 'node:fs'; import { existsSync } from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { beforeAll, describe, expect, it } from 'vitest'; import { beforeAll, describe, expect, it } from 'vitest';
import { resolveCredentials } from '../../src/lib/cookies.js';
import { TwitterClient } from '../../src/lib/twitter-client.js';
type RunResult = { exitCode: number; stdout: string; stderr: string; signal: NodeJS.Signals | null }; type RunResult = { exitCode: number; stdout: string; stderr: string; signal: NodeJS.Signals | null };
+3 -2
View File
@@ -202,18 +202,19 @@ describe('TwitterClient getUserAboutAccount', () => {
}), }),
}); });
const client = new TwitterClient({ cookies: validCookies }); const client = new TwitterClient({ relayBaseUrl: 'http://relay.test', profileName: 'test-profile' });
await client.getUserAboutAccount('testuser'); await client.getUserAboutAccount('testuser');
expect(mockFetch).toHaveBeenCalledTimes(1); expect(mockFetch).toHaveBeenCalledTimes(1);
const [url, options] = mockFetch.mock.calls[0] as [string, RequestInit]; const [url, options] = mockFetch.mock.calls[0] as [string, RequestInit];
expect(url).toContain('http://relay.test/i/api/graphql/');
expect(url).toContain('/AboutAccountQuery?'); expect(url).toContain('/AboutAccountQuery?');
expect(url).toContain('variables='); expect(url).toContain('variables=');
expect(url).toContain('testuser'); expect(url).toContain('testuser');
expect(options.method).toBe('GET'); expect(options.method).toBe('GET');
expect(options.headers).toMatchObject({ expect(options.headers).toMatchObject({
'x-csrf-token': 'test_ct0_token', 'x-profile-name': 'test-profile',
}); });
}); });
}); });