← back to Dear Bubbe Nextjs
archived/social-posters/playwright-twitter-poster.js
171 lines
const { chromium } = require('playwright');
const fs = require('fs').promises;
const path = require('path');
// Configuration
const TWITTER_USERNAME = 'DearBubbe';
const TWITTER_PASSWORD = '*Twitteraccess911*';
const COOKIES_PATH = path.join(__dirname, 'twitter-cookies.json');
const POSTED_IDS_PATH = path.join(__dirname, 'posted-twitter-ids.json');
// Load cookies
async function loadCookies(context) {
try {
const cookies = JSON.parse(await fs.readFile(COOKIES_PATH, 'utf8'));
await context.addCookies(cookies);
return true;
} catch (e) {
console.log('No saved cookies found');
return false;
}
}
// Save cookies
async function saveCookies(context) {
const cookies = await context.cookies();
await fs.writeFile(COOKIES_PATH, JSON.stringify(cookies, null, 2));
}
// Main posting function
async function postToTwitter(text, messageId = null) {
console.log('\n🐦 POSTING TO TWITTER');
console.log('Text:', text.substring(0, 100) + '...');
const browser = await chromium.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
try {
const context = await browser.newContext({
viewport: { width: 1280, height: 720 },
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
});
const page = await context.newPage();
// Load cookies if available
const cookiesLoaded = await loadCookies(context);
// Go to Twitter
await page.goto('https://twitter.com/home', { waitUntil: 'networkidle' });
// Check if we need to login
const needsLogin = await page.url().includes('login') || await page.url().includes('i/flow');
if (needsLogin && !cookiesLoaded) {
console.log('Logging in to Twitter...');
// Enter username
await page.waitForSelector('input[autocomplete="username"]', { timeout: 10000 });
await page.fill('input[autocomplete="username"]', TWITTER_USERNAME);
await page.press('input[autocomplete="username"]', 'Enter');
// Wait for password field
await page.waitForTimeout(2000);
// Enter password
await page.waitForSelector('input[type="password"]', { timeout: 10000 });
await page.fill('input[type="password"]', TWITTER_PASSWORD);
await page.press('input[type="password"]', 'Enter');
// Wait for login to complete
await page.waitForURL('**/home', { timeout: 15000 });
// Save cookies
await saveCookies(context);
console.log('Login successful, cookies saved');
}
// Click compose tweet button
await page.waitForSelector('[data-testid="SideNav_NewTweet_Button"]', { timeout: 10000 });
await page.click('[data-testid="SideNav_NewTweet_Button"]');
// Wait for compose modal
await page.waitForTimeout(1000);
// Type the tweet
await page.waitForSelector('[data-testid="tweetTextarea_0"]', { timeout: 10000 });
await page.fill('[data-testid="tweetTextarea_0"]', text);
// Wait a moment
await page.waitForTimeout(1000);
// Click Tweet button
await page.click('[data-testid="tweetButton"]');
// Wait for tweet to be posted
await page.waitForTimeout(3000);
// Mark as posted
if (messageId) {
let postedIds = [];
try {
postedIds = JSON.parse(await fs.readFile(POSTED_IDS_PATH, 'utf8'));
} catch (e) {}
postedIds.push(messageId);
await fs.writeFile(POSTED_IDS_PATH, JSON.stringify(postedIds, null, 2));
}
console.log('✅ Tweet posted successfully!');
await context.close();
await browser.close();
return true;
} catch (error) {
console.error('❌ Error posting tweet:', error.message);
await browser.close();
return false;
}
}
// Post from queue
async function postFromQueue() {
try {
const queuePath = '/root/Projects/dear-bubbe-admin/logs/twitter-queue.json';
const queue = JSON.parse(await fs.readFile(queuePath, 'utf8'));
if (queue.length === 0) {
console.log('No tweets in queue');
return;
}
// Get first unposted tweet
const toPost = queue.find(t => t.status === 'queued');
if (!toPost) {
console.log('All tweets already posted');
return;
}
// Post it
const success = await postToTwitter(toPost.text);
if (success) {
// Update queue
toPost.status = 'posted';
toPost.postedAt = new Date().toISOString();
await fs.writeFile(queuePath, JSON.stringify(queue, null, 2));
console.log('Queue updated');
}
return success;
} catch (error) {
console.error('Error posting from queue:', error);
return false;
}
}
module.exports = {
postToTwitter,
postFromQueue
};
// If run directly, post from queue
if (require.main === module) {
postFromQueue()
.then(result => {
console.log('Result:', result ? 'SUCCESS' : 'FAILED');
process.exit(result ? 0 : 1);
});
}