← back to Dear Bubbe Nextjs

archived/social-posters/playwright-bluesky-poster.js

172 lines

const { chromium } = require('playwright');
const fs = require('fs').promises;
const path = require('path');

// Configuration
const BLUESKY_HANDLE = 'dearbubbe.bsky.social';
const BLUESKY_PASSWORD = '*Blueaccess911*';
const COOKIES_PATH = path.join(__dirname, 'bluesky-cookies.json');
const POSTED_IDS_PATH = path.join(__dirname, 'posted-bluesky-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 postToBluesky(text, messageId = null) {
    console.log('\n🦋 POSTING TO BLUESKY');
    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 Bluesky
        await page.goto('https://bsky.app', { waitUntil: 'networkidle' });
        
        // Check if we need to login
        const needsLogin = !await page.url().includes('/home') && !cookiesLoaded;
        
        if (needsLogin) {
            console.log('Logging in to Bluesky...');
            
            // Click Sign in
            await page.waitForSelector('button:has-text("Sign in")', { timeout: 10000 });
            await page.click('button:has-text("Sign in")');
            
            // Enter handle
            await page.waitForSelector('input[placeholder*="Username or email"]', { timeout: 10000 });
            await page.fill('input[placeholder*="Username or email"]', BLUESKY_HANDLE);
            
            // Enter password
            await page.fill('input[type="password"]', BLUESKY_PASSWORD);
            
            // Click login
            await page.click('button[type="submit"]');
            
            // Wait for home page
            await page.waitForURL('**/home', { timeout: 15000 });
            
            // Save cookies
            await saveCookies(context);
            console.log('Login successful, cookies saved');
        }
        
        // Click compose button
        await page.waitForSelector('button[aria-label*="Compose"]', { timeout: 10000 });
        await page.click('button[aria-label*="Compose"]');
        
        // Wait for compose modal
        await page.waitForTimeout(1000);
        
        // Type the post
        await page.waitForSelector('textarea[placeholder*="What"]', { timeout: 10000 });
        await page.fill('textarea[placeholder*="What"]', text);
        
        // Wait a moment
        await page.waitForTimeout(1000);
        
        // Click Post button
        await page.click('button:has-text("Post")');
        
        // Wait for post to complete
        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('✅ Bluesky post successful!');
        await context.close();
        await browser.close();
        return true;
        
    } catch (error) {
        console.error('❌ Error posting to Bluesky:', error.message);
        await browser.close();
        return false;
    }
}

// Post from queue
async function postFromQueue() {
    try {
        const queuePath = '/root/Projects/dear-bubbe-admin/logs/bluesky-queue.json';
        const queue = JSON.parse(await fs.readFile(queuePath, 'utf8'));
        
        if (queue.length === 0) {
            console.log('No posts in queue');
            return;
        }
        
        // Get first unposted
        const toPost = queue.find(p => p.status === 'queued');
        if (!toPost) {
            console.log('All posts already sent');
            return;
        }
        
        // Post it
        const success = await postToBluesky(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 = {
    postToBluesky,
    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);
        });
}