Add X integration skill (#52)

This commit is contained in:
BaiJunjie
2026-02-04 07:27:04 +09:00
committed by GitHub
parent 3084fab45d
commit c9ca34a518
11 changed files with 1450 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
#!/usr/bin/env npx tsx
/**
* X Integration - Post Tweet
* Usage: echo '{"content":"Hello world"}' | npx tsx post.ts
*/
import { getBrowserContext, runScript, validateContent, config, ScriptResult } from '../lib/browser.js';
interface PostInput {
content: string;
}
async function postTweet(input: PostInput): Promise<ScriptResult> {
const { content } = input;
const validationError = validateContent(content, 'Tweet');
if (validationError) return validationError;
let context = null;
try {
context = await getBrowserContext();
const page = context.pages()[0] || await context.newPage();
await page.goto('https://x.com/home', { timeout: config.timeouts.navigation, waitUntil: 'domcontentloaded' });
await page.waitForTimeout(config.timeouts.pageLoad);
// Check if logged in
const isLoggedIn = await page.locator('[data-testid="SideNav_AccountSwitcher_Button"]').isVisible().catch(() => false);
if (!isLoggedIn) {
const onLoginPage = await page.locator('input[autocomplete="username"]').isVisible().catch(() => false);
if (onLoginPage) {
return { success: false, message: 'X login expired. Run /x-integration to re-authenticate.' };
}
}
// Find and fill tweet input
const tweetInput = page.locator('[data-testid="tweetTextarea_0"]');
await tweetInput.waitFor({ timeout: config.timeouts.elementWait * 2 });
await tweetInput.click();
await page.waitForTimeout(config.timeouts.afterClick / 2);
await tweetInput.fill(content);
await page.waitForTimeout(config.timeouts.afterFill);
// Click post button
const postButton = page.locator('[data-testid="tweetButtonInline"]');
await postButton.waitFor({ timeout: config.timeouts.elementWait });
const isDisabled = await postButton.getAttribute('aria-disabled');
if (isDisabled === 'true') {
return { success: false, message: 'Post button disabled. Content may be empty or exceed character limit.' };
}
await postButton.click();
await page.waitForTimeout(config.timeouts.afterSubmit);
return {
success: true,
message: `Tweet posted: ${content.slice(0, 50)}${content.length > 50 ? '...' : ''}`
};
} finally {
if (context) await context.close();
}
}
runScript<PostInput>(postTweet);