Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/cli/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -31,8 +32,10 @@ export const KNOWN_COMMANDS = new Set([
'unfollow',
'following',
'followers',
'like',
'likes',
'lists',
'unlike',
'list-timeline',
'home',
'user-tweets',
Expand Down Expand Up @@ -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);
Expand Down
81 changes: 81 additions & 0 deletions src/commands/like.ts
Original file line number Diff line number Diff line change
@@ -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-id-or-url...>', '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-id-or-url...>', '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);
}
});
}