diff --git a/src/cli/program.ts b/src/cli/program.ts index 6c5c605..e061fda 100644 --- a/src/cli/program.ts +++ b/src/cli/program.ts @@ -4,6 +4,7 @@ import { registerCheckCommand } from '../commands/check.js'; import { registerFollowCommands } from '../commands/follow.js'; import { registerHelpCommand } from '../commands/help.js'; import { registerHomeCommand } from '../commands/home.js'; +import { registerLikeCommands } from '../commands/like.js'; import { registerListsCommand } from '../commands/lists.js'; import { registerNewsCommand } from '../commands/news.js'; import { registerPostCommands } from '../commands/post.js'; @@ -31,8 +32,10 @@ export const KNOWN_COMMANDS = new Set([ 'unfollow', 'following', 'followers', + 'like', 'likes', 'lists', + 'unlike', 'list-timeline', 'home', 'user-tweets', @@ -146,6 +149,7 @@ export function createProgram(ctx: CliContext): Command { registerBookmarksCommand(program, ctx); registerUnbookmarkCommand(program, ctx); registerFollowCommands(program, ctx); + registerLikeCommands(program, ctx); registerListsCommand(program, ctx); registerHomeCommand(program, ctx); registerUserCommands(program, ctx); diff --git a/src/commands/like.ts b/src/commands/like.ts new file mode 100644 index 0000000..01ee11a --- /dev/null +++ b/src/commands/like.ts @@ -0,0 +1,81 @@ +import type { Command } from 'commander'; +import type { CliContext } from '../cli/shared.js'; +import { TwitterClient } from '../lib/twitter-client.js'; + +export function registerLikeCommands(program: Command, ctx: CliContext): void { + program + .command('like') + .description('Like tweets') + .argument('', 'Tweet IDs or URLs to like') + .action(async (tweetIdOrUrls: string[]) => { + 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 }); + let failures = 0; + + for (const input of tweetIdOrUrls) { + const tweetId = ctx.extractTweetId(input); + const result = await client.like(tweetId); + if (result.success) { + console.log(`${ctx.p('ok')}Liked tweet ${tweetId}`); + } else { + failures += 1; + console.error(`${ctx.p('err')}Failed to like tweet ${tweetId}: ${result.error}`); + } + } + + if (failures > 0) { + process.exit(1); + } + }); + + program + .command('unlike') + .description('Unlike tweets') + .argument('', 'Tweet IDs or URLs to unlike') + .action(async (tweetIdOrUrls: string[]) => { + 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 }); + let failures = 0; + + for (const input of tweetIdOrUrls) { + const tweetId = ctx.extractTweetId(input); + const result = await client.unlike(tweetId); + if (result.success) { + console.log(`${ctx.p('ok')}Unliked tweet ${tweetId}`); + } else { + failures += 1; + console.error(`${ctx.p('err')}Failed to unlike tweet ${tweetId}: ${result.error}`); + } + } + + if (failures > 0) { + process.exit(1); + } + }); +}