fix(engine): disable Sweetistics fallback when forcing graphql

This commit is contained in:
Peter Steinberger
2025-12-26 15:10:10 +01:00
parent 9a4465f05d
commit dee304235b
5 changed files with 30 additions and 11 deletions
+1
View File
@@ -14,6 +14,7 @@
- `mentions`: no hardcoded user; defaults to authenticated user or accepts `--user @handle`.
- GraphQL query ID updater: correctly pairs `operationName` ↔ `queryId` (CreateTweet/CreateRetweet/etc).
- `build:dist`: copies `src/lib/query-ids.json` into `dist/lib/query-ids.json` (keeps `dist/` in sync).
- `--engine graphql`: strict GraphQL-only (disables Sweetistics fallback).
## 0.1.1 — 2025-12-26
+2
View File
@@ -57,6 +57,8 @@ bird query-ids --fresh
- `--engine graphql` — use Twitter/X GraphQL with cookies (Chrome/Firefox/env/flags).
- `--engine sweetistics` — use Sweetistics API key (no browser cookies needed).
Note: fallback to Sweetistics only happens in `--engine auto`. `--engine graphql` is strict (no fallback).
Global options:
- `--timeout <ms>`: abort requests after the given timeout (milliseconds).
- `--plain`: stable output (no emoji, no color).
+15 -10
View File
@@ -17,7 +17,12 @@ import JSON5 from 'json5';
import kleur from 'kleur';
import { resolveCliInvocation } from './lib/cli-args.js';
import { resolveCredentials } from './lib/cookies.js';
import { type EngineMode, resolveEngineMode, shouldUseSweetistics } from './lib/engine.js';
import {
type EngineMode,
resolveEngineMode,
shouldAllowSweetisticsFallback,
shouldUseSweetistics,
} from './lib/engine.js';
import { extractTweetId } from './lib/extract-tweet-id.js';
import { mentionsQueryFromUserOption, normalizeHandle } from './lib/normalize-handle.js';
import {
@@ -429,7 +434,7 @@ program
if (result.success) {
console.log(`${p('ok')}Tweet posted successfully!`);
console.log(`${l('url')}https://x.com/i/status/${result.tweetId}`);
} else if (sweetistics.apiKey) {
} else if (shouldAllowSweetisticsFallback(engine, Boolean(sweetistics.apiKey))) {
console.error(`${p('warn')}GraphQL tweet failed (${result.error}); trying Sweetistics fallback...`);
const fallback = await new SweetisticsClient({
baseUrl: sweetistics.baseUrl,
@@ -558,7 +563,7 @@ program
if (result.success) {
console.log(`${p('ok')}Reply posted successfully!`);
console.log(`${l('url')}https://x.com/i/status/${result.tweetId}`);
} else if (sweetistics.apiKey) {
} else if (shouldAllowSweetisticsFallback(engine, Boolean(sweetistics.apiKey))) {
console.error(`${p('warn')}GraphQL reply failed (${result.error}); trying Sweetistics fallback...`);
const fallback = await new SweetisticsClient({
baseUrl: sweetistics.baseUrl,
@@ -657,7 +662,7 @@ program
}
console.log(formatStatsLine(result.tweet, output));
}
} else if (sweetistics.apiKey) {
} else if (shouldAllowSweetisticsFallback(engine, Boolean(sweetistics.apiKey))) {
console.error(`${p('warn')}GraphQL read failed (${result.error}); trying Sweetistics fallback...`);
const fallback = await new SweetisticsClient({
baseUrl: sweetistics.baseUrl,
@@ -741,7 +746,7 @@ program
if (result.success && result.tweets) {
printTweets(result.tweets, { json: cmdOpts.json, emptyMessage: 'No replies found.' });
} else if (sweetistics.apiKey) {
} else if (shouldAllowSweetisticsFallback(engine, Boolean(sweetistics.apiKey))) {
console.error(`${p('warn')}GraphQL replies failed (${result.error}); trying Sweetistics fallback...`);
const fallback = await new SweetisticsClient({
baseUrl: sweetistics.baseUrl,
@@ -816,7 +821,7 @@ program
if (result.success && result.tweets) {
printTweets(result.tweets, { json: cmdOpts.json, emptyMessage: 'No thread tweets found.' });
} else if (sweetistics.apiKey) {
} else if (shouldAllowSweetisticsFallback(engine, Boolean(sweetistics.apiKey))) {
console.error(`${p('warn')}GraphQL thread failed (${result.error}); trying Sweetistics fallback...`);
const fallback = await new SweetisticsClient({
baseUrl: sweetistics.baseUrl,
@@ -893,7 +898,7 @@ program
if (result.success && result.tweets) {
printTweets(result.tweets, { json: cmdOpts.json, emptyMessage: 'No tweets found.' });
} else if (sweetistics.apiKey) {
} else if (shouldAllowSweetisticsFallback(engine, Boolean(sweetistics.apiKey))) {
console.error(`${p('warn')}GraphQL search failed (${result.error}); trying Sweetistics fallback...`);
const fallback = await new SweetisticsClient({
baseUrl: sweetistics.baseUrl,
@@ -988,7 +993,7 @@ program
const who = await client.getCurrentUser();
const handle = normalizeHandle(who.user?.username);
if (!handle) {
if (sweetistics.apiKey) {
if (shouldAllowSweetisticsFallback(engine, Boolean(sweetistics.apiKey))) {
console.error(
`${p('warn')}Could not determine current user (${who.error ?? 'Unknown error'}); trying Sweetistics fallback...`,
);
@@ -1018,7 +1023,7 @@ program
if (result.success && result.tweets) {
printTweets(result.tweets, { json: cmdOpts.json, emptyMessage: 'No mentions found.' });
} else if (sweetistics.apiKey) {
} else if (shouldAllowSweetisticsFallback(engine, Boolean(sweetistics.apiKey))) {
console.error(`${p('warn')}GraphQL mentions failed (${result.error}); trying Sweetistics fallback...`);
const fallback = await new SweetisticsClient({
baseUrl: sweetistics.baseUrl,
@@ -1115,7 +1120,7 @@ program
console.log(`${l('credentials')}${credentialSource}`);
} else {
// Fallback: try Sweetistics if available
if (sweetistics.apiKey) {
if (shouldAllowSweetisticsFallback(engine, Boolean(sweetistics.apiKey))) {
const fallback = await new SweetisticsClient({
baseUrl: sweetistics.baseUrl,
apiKey: sweetistics.apiKey,
+4
View File
@@ -13,3 +13,7 @@ export function shouldUseSweetistics(engine: EngineMode, _hasApiKey: boolean): b
if (engine === 'graphql') return false;
return false; // auto: GraphQL primary, Sweetistics only as fallback
}
export function shouldAllowSweetisticsFallback(engine: EngineMode, hasApiKey: boolean): boolean {
return engine === 'auto' && hasApiKey;
}
+8 -1
View File
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest';
import { resolveEngineMode, shouldUseSweetistics } from '../src/lib/engine.js';
import { resolveEngineMode, shouldAllowSweetisticsFallback, shouldUseSweetistics } from '../src/lib/engine.js';
describe('engine', () => {
it('normalizes engine values and defaults to auto', () => {
@@ -19,4 +19,11 @@ describe('engine', () => {
expect(shouldUseSweetistics('sweetistics', false)).toBe(true);
expect(shouldUseSweetistics('graphql', true)).toBe(false);
});
it('only allows Sweetistics fallback in auto mode', () => {
expect(shouldAllowSweetisticsFallback('auto', true)).toBe(true);
expect(shouldAllowSweetisticsFallback('auto', false)).toBe(false);
expect(shouldAllowSweetisticsFallback('graphql', true)).toBe(false);
expect(shouldAllowSweetisticsFallback('sweetistics', true)).toBe(false);
});
});