test(cli): validate mentions handle
This commit is contained in:
@@ -27,6 +27,7 @@ bird replies 1234567890123456789
|
||||
# Search + mentions
|
||||
bird search "from:steipete" -n 5
|
||||
bird mentions -n 5
|
||||
bird mentions --user @steipete -n 5
|
||||
```
|
||||
|
||||
## Commands
|
||||
@@ -38,7 +39,7 @@ bird mentions -n 5
|
||||
- `bird replies <tweet-id-or-url> [--json]` — list replies to a tweet.
|
||||
- `bird thread <tweet-id-or-url> [--json]` — show the full conversation thread.
|
||||
- `bird search "<query>" [-n count] [--json]` — search for tweets matching a query.
|
||||
- `bird mentions [-n count] [--json]` — find tweets mentioning @clawdbot.
|
||||
- `bird mentions [-n count] [--user @handle] [--json]` — find tweets mentioning a user (defaults to the authenticated user).
|
||||
- `bird whoami` — print which Twitter account your cookies belong to.
|
||||
- `bird check` — show which credentials are available and where they were sourced from.
|
||||
|
||||
@@ -92,6 +93,10 @@ Environment shortcuts:
|
||||
- `--json` prints raw tweet objects for read/replies/thread/search/mentions.
|
||||
- `read` returns full text for Notes and Articles when present.
|
||||
|
||||
## Version
|
||||
|
||||
`bird --version` prints `package.json` version plus current git sha when available, e.g. `0.1.1 (3df7969b)`.
|
||||
|
||||
## Media uploads (Sweetistics only)
|
||||
|
||||
- Attach media with `--media` (repeatable) and optional `--alt` per item.
|
||||
|
||||
+8
-3
@@ -19,7 +19,7 @@ import { resolveCliInvocation } from './lib/cli-args.js';
|
||||
import { resolveCredentials } from './lib/cookies.js';
|
||||
import { type EngineMode, resolveEngineMode, shouldUseSweetistics } from './lib/engine.js';
|
||||
import { extractTweetId } from './lib/extract-tweet-id.js';
|
||||
import { normalizeHandle } from './lib/normalize-handle.js';
|
||||
import { mentionsQueryFromUserOption, normalizeHandle } from './lib/normalize-handle.js';
|
||||
import { SweetisticsClient } from './lib/sweetistics-client.js';
|
||||
import { type TweetData, TwitterClient } from './lib/twitter-client.js';
|
||||
import { getCliVersion } from './lib/version.js';
|
||||
@@ -817,8 +817,13 @@ program
|
||||
const engine = resolveEngineMode(opts.engine);
|
||||
const useSweetistics = shouldUseSweetistics(engine, Boolean(sweetistics.apiKey));
|
||||
|
||||
const explicitHandle = normalizeHandle(cmdOpts.user);
|
||||
let query: string | null = explicitHandle ? `@${explicitHandle}` : null;
|
||||
const fromUserOpt = mentionsQueryFromUserOption(cmdOpts.user);
|
||||
if (fromUserOpt.error) {
|
||||
console.error(`❌ ${fromUserOpt.error}`);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
let query: string | null = fromUserOpt.query;
|
||||
|
||||
if (useSweetistics) {
|
||||
if (!sweetistics.apiKey) {
|
||||
|
||||
@@ -11,3 +11,20 @@ export function normalizeHandle(input?: string | null): string | null {
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
export function mentionsQueryFromUserOption(userOption?: string | null): {
|
||||
query: string | null;
|
||||
error: string | null;
|
||||
} {
|
||||
if (typeof userOption === 'undefined') return { query: null, error: null };
|
||||
|
||||
const handle = normalizeHandle(userOption);
|
||||
if (!handle) {
|
||||
return {
|
||||
query: null,
|
||||
error: 'Invalid --user handle. Expected something like @steipete (letters, digits, underscore; max 15).',
|
||||
};
|
||||
}
|
||||
|
||||
return { query: `@${handle}`, error: null };
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { normalizeHandle } from '../src/lib/normalize-handle.js';
|
||||
import { mentionsQueryFromUserOption, normalizeHandle } from '../src/lib/normalize-handle.js';
|
||||
|
||||
describe('normalizeHandle', () => {
|
||||
it('accepts bare handle', () => {
|
||||
@@ -22,3 +22,20 @@ describe('normalizeHandle', () => {
|
||||
expect(normalizeHandle('a'.repeat(16))).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('mentionsQueryFromUserOption', () => {
|
||||
it('returns null query when option omitted', () => {
|
||||
expect(mentionsQueryFromUserOption(undefined)).toEqual({ query: null, error: null });
|
||||
});
|
||||
|
||||
it('returns normalized @query for valid handle', () => {
|
||||
expect(mentionsQueryFromUserOption('@steipete')).toEqual({ query: '@steipete', error: null });
|
||||
expect(mentionsQueryFromUserOption(' steipete ')).toEqual({ query: '@steipete', error: null });
|
||||
});
|
||||
|
||||
it('returns error for invalid handle', () => {
|
||||
const result = mentionsQueryFromUserOption('@stei-pete');
|
||||
expect(result.query).toBeNull();
|
||||
expect(result.error).toMatch(/Invalid --user handle/);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user