UNPKG

build-in-public-bot

Version:

AI-powered CLI bot for automating build-in-public tweets with code screenshots

247 lines 10.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.TwitterPlaywrightService = void 0; const errors_1 = require("../utils/errors"); const logger_1 = require("../utils/logger"); class TwitterPlaywrightService { browser = null; context = null; async launchBrowser(_headless = false) { let chromium; try { const playwright = await Promise.resolve(`${'playwright'}`).then(s => __importStar(require(s))); chromium = playwright.chromium; } catch (error) { throw new errors_1.TwitterError('Playwright is not installed. Run: npm install playwright'); } this.browser = await chromium.launch({ headless: false, args: [ '--disable-blink-features=AutomationControlled', '--disable-features=IsolateOrigins,site-per-process', '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-web-security', '--disable-features=site-per-process', '--disable-site-isolation-trials', '--disable-features=AudioServiceOutOfProcess', '--disable-features=IsolateOrigins', '--disable-features=site-per-process', '--disable-blink-features', '--disable-ipc-flooding-protection', '--disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose', '--disable-features=CrossSiteDocumentBlockingIfIsolating,CrossSiteDocumentBlockingAlways', '--disable-features=StrictOriginIsolation', '--flag-switches-begin', '--disable-site-isolation-trials', '--flag-switches-end' ] }); this.context = await this.browser.newContext({ viewport: { width: 1920, height: 1080 }, userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', locale: 'en-US', timezoneId: 'America/New_York', deviceScaleFactor: 1, hasTouch: false, extraHTTPHeaders: { 'Accept-Language': 'en-US,en;q=0.9', 'Accept-Encoding': 'gzip, deflate, br', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8', 'Connection': 'keep-alive', 'Upgrade-Insecure-Requests': '1', 'Sec-Fetch-Dest': 'document', 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-Site': 'none', 'Sec-Fetch-User': '?1', 'Cache-Control': 'max-age=0' } }); await this.applyStealthPatches(); } async applyStealthPatches() { if (!this.context) return; await this.context.addInitScript(() => { Object.defineProperty(navigator, 'webdriver', { get: () => undefined }); Object.defineProperty(navigator, 'plugins', { get: () => { const arr = [ { name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer' }, { name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai' }, { name: 'Native Client', filename: 'internal-nacl-plugin' } ]; return Object.assign(arr, { namedItem: (name) => arr.find(p => p.name === name), refresh: () => { } }); } }); const originalQuery = window.navigator.permissions.query; window.navigator.permissions.query = (parameters) => { if (parameters.name === 'notifications') { return Promise.resolve({ state: Notification.permission }); } return originalQuery(parameters); }; window.chrome = { runtime: {}, loadTimes: () => ({}), csi: () => ({}) }; const originalError = Error; window.Error = new Proxy(originalError, { construct(target, args) { const error = new target(...args); if (error.stack && error.stack.includes('CDP')) { error.stack = error.stack.replace(/CDP/g, ''); } return error; } }); }); } async postTweet(username, password, text) { let page = null; try { await this.launchBrowser(); if (!this.context) { throw new errors_1.TwitterError('Browser context not initialized'); } page = await this.context.newPage(); logger_1.logger.info('Navigating to Twitter...'); await page.goto('https://twitter.com/login', { waitUntil: 'networkidle', timeout: 30000 }); await page.waitForTimeout(2000 + Math.random() * 2000); logger_1.logger.info('Logging in...'); const usernameInput = page.locator('input[autocomplete="username"]'); await usernameInput.waitFor({ state: 'visible', timeout: 10000 }); await usernameInput.click(); await this.humanType(page, username); await page.keyboard.press('Enter'); await page.waitForTimeout(2000 + Math.random() * 1000); const passwordInput = page.locator('input[type="password"]'); await passwordInput.waitFor({ state: 'visible', timeout: 10000 }); await passwordInput.click(); await this.humanType(page, password); await page.keyboard.press('Enter'); await page.waitForTimeout(5000); const homeIndicator = page.locator('[data-testid="primaryColumn"]'); try { await homeIndicator.waitFor({ state: 'visible', timeout: 10000 }); } catch { throw new errors_1.TwitterError('Login failed - could not find home timeline'); } logger_1.logger.info('Warming up session...'); await this.warmUpSession(page); logger_1.logger.info('Composing tweet...'); await page.goto('https://twitter.com/compose/tweet', { waitUntil: 'networkidle' }); await page.waitForTimeout(2000); const tweetBox = page.locator('[data-testid="tweetTextarea_0"]'); await tweetBox.waitFor({ state: 'visible', timeout: 10000 }); await tweetBox.click(); await this.humanType(page, text); await page.waitForTimeout(2000 + Math.random() * 1000); const tweetButton = page.locator('[data-testid="tweetButtonInline"]'); await tweetButton.click(); await page.waitForTimeout(3000); const currentUrl = page.url(); let tweetId = Date.now().toString(); if (currentUrl.includes('/status/')) { const match = currentUrl.match(/status\/(\d+)/); if (match) { tweetId = match[1]; } } return { id: tweetId, url: `https://twitter.com/${username}/status/${tweetId}` }; } catch (error) { throw new errors_1.TwitterError('Failed to post tweet with Playwright', error); } finally { if (page) { await page.close(); } await this.cleanup(); } } async humanType(page, text) { for (const char of text) { await page.keyboard.type(char); await page.waitForTimeout(50 + Math.random() * 100); if (Math.random() < 0.05) { await page.waitForTimeout(300 + Math.random() * 500); } } } async warmUpSession(page) { for (let i = 0; i < 3; i++) { await page.evaluate(() => { window.scrollBy(0, 200 + Math.random() * 300); }); await page.waitForTimeout(1000 + Math.random() * 1000); } if (Math.random() < 0.1) { await page.goto('https://twitter.com/notifications'); await page.waitForTimeout(2000); await page.goto('https://twitter.com/home'); } } async cleanup() { if (this.context) { await this.context.close(); } if (this.browser) { await this.browser.close(); } this.context = null; this.browser = null; } } exports.TwitterPlaywrightService = TwitterPlaywrightService; //# sourceMappingURL=twitter-playwright.js.map