← back to Dear Bubbe Admin

backend/social-post-handler.js

233 lines

const fs = require('fs').promises;
const path = require('path');

// Bubbe intro phrases for variety
const BUBBE_INTROS = [
    "Bubbe tells it like it is",
    "Bubbe's got no filter",
    "Bubbe says what you're thinking",
    "Bubbe drops truth bombs",
    "Bubbe keeps it real",
    "Bubbe's wisdom hits different",
    "Bubbe doesn't sugarcoat",
    "Bubbe serves reality checks",
    "Bubbe's brutal honesty",
    "Bubbe speaks facts",
    "Bubbe's savage advice",
    "Bubbe pulls no punches",
    "Bubbe's unfiltered truth",
    "Bubbe's raw wisdom",
    "Bubbe's tough love",
    "Bubbe's straight talk",
    "Bubbe's no-nonsense advice",
    "Bubbe's reality check",
    "Bubbe's harsh truth",
    "Bubbe tells you straight"
];

// Name anonymization function
function anonymizeText(text) {
    const nameReplacements = {
        // Male names
        'john': 'David', 'mike': 'Michael', 'steve': 'Robert', 'james': 'William',
        'david': 'John', 'robert': 'James', 'michael': 'Thomas', 'william': 'Charles',
        'richard': 'Joseph', 'joseph': 'Daniel', 'thomas': 'Paul', 'charles': 'Mark',
        
        // Female names  
        'mary': 'Sarah', 'jennifer': 'Jessica', 'linda': 'Emma', 'patricia': 'Emily',
        'elizabeth': 'Anna', 'susan': 'Rachel', 'jessica': 'Rebecca', 'sarah': 'Hannah',
        'karen': 'Michelle', 'nancy': 'Laura', 'betty': 'Amanda', 'dorothy': 'Nicole',
        
        // Spouse/partner references
        'my husband': 'my partner', 'my wife': 'my partner', 
        'my boyfriend': 'my partner', 'my girlfriend': 'my partner'
    };
    
    let anonymized = text;
    
    for (const [original, replacement] of Object.entries(nameReplacements)) {
        const regex = new RegExp(`\\b${original}\\b`, 'gi');
        anonymized = anonymized.replace(regex, replacement);
    }
    
    // Remove sensitive info
    anonymized = anonymized.replace(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, '[email]');
    anonymized = anonymized.replace(/\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g, '[phone]');
    anonymized = anonymized.replace(/\d+\s+[A-Za-z]+\s+(Street|St|Avenue|Ave|Road|Rd)/gi, '[address]');
    
    return anonymized;
}

// Get random intro
function getRandomIntro() {
    return BUBBE_INTROS[Math.floor(Math.random() * BUBBE_INTROS.length)];
}

// Format post for Twitter
function formatTwitterPost(userMessage, bubbeResponse) {
    const intro = getRandomIntro();
    const anonymizedUser = anonymizeText(userMessage).substring(0, 60);
    const anonymizedResponse = anonymizeText(bubbeResponse).substring(0, 100);
    
    return `${intro}:\n\n"${anonymizedResponse}..." #BubbeAI #Bubbe #NoFilter #JewishGrandma\n\nVisit Bubbe.AI`;
}

// Format post for Bluesky
function formatBlueskyPost(userMessage, bubbeResponse) {
    const intro = getRandomIntro();
    const anonymizedResponse = anonymizeText(bubbeResponse).substring(0, 120);
    
    return `${intro}: "${anonymizedResponse}..." #BubbeAI #Bubbe\n\nVisit Bubbe.AI`.substring(0, 300);
}

// Post to Twitter using official API v2
async function postToTwitter(text) {
    try {
        // Log the attempt
        console.log('[TWITTER] Posting via API v2:', text.substring(0, 50) + '...');
        
        // Use the official Twitter API
        const { postTweet } = require('/root/Projects/dear-bubbe-nextjs/lib/twitter-v2-official.js');
        const result = await postTweet(text);
        
        if (result.success) {
            console.log('[TWITTER] Successfully posted! Tweet ID:', result.tweetId);
            console.log('[TWITTER] URL:', `https://x.com/DearBubbe/status/${result.tweetId}`);
            
            // Log successful post
            const logEntry = {
                platform: 'twitter',
                text,
                timestamp: new Date().toISOString(),
                status: 'posted',
                tweetId: result.tweetId,
                url: `https://x.com/DearBubbe/status/${result.tweetId}`
            };
            
            // Save to log file
            const logsPath = path.join(__dirname, '../logs/twitter-posts.json');
            let logs = [];
            try {
                logs = JSON.parse(await fs.readFile(logsPath, 'utf8'));
            } catch (e) {
                // File doesn't exist
            }
            logs.push(logEntry);
            await fs.writeFile(logsPath, JSON.stringify(logs, null, 2));
            
            return true;
        } else {
            console.error('[TWITTER] Failed to post:', result.error);
            return false;
        }
    } catch (error) {
        console.error('[TWITTER] Error:', error);
        return false;
    }
}

// Post to Bluesky using API or webhook
async function postToBluesky(text) {
    try {
        // Log the attempt
        console.log('[BLUESKY] Posting:', text.substring(0, 50) + '...');
        
        // TODO: Add actual Bluesky API implementation here
        // For now, just log it
        const logEntry = {
            platform: 'bluesky',
            text,
            timestamp: new Date().toISOString(),
            status: 'queued'
        };
        
        // Save to queue file
        const queuePath = path.join(__dirname, '../logs/bluesky-queue.json');
        let queue = [];
        try {
            queue = JSON.parse(await fs.readFile(queuePath, 'utf8'));
        } catch (e) {
            // File doesn't exist
        }
        queue.push(logEntry);
        await fs.writeFile(queuePath, JSON.stringify(queue, null, 2));
        
        return true;
    } catch (error) {
        console.error('[BLUESKY] Error:', error);
        return false;
    }
}

// Main handler for immediate posting
async function handleImmediateSocialPost(data) {
    const { userMessage, bubbeResponse, messageId, timestamp, userId, location, userName } = data;
    
    console.log(`\n=== IMMEDIATE SOCIAL POST REQUEST ===`);
    console.log(`Time: ${timestamp}`);
    console.log(`User: ${userName} (${location})`);
    console.log(`Message ID: ${messageId}`);
    console.log(`User said: "${userMessage.substring(0, 50)}..."`);
    console.log(`Bubbe said: "${bubbeResponse.substring(0, 50)}..."`);
    
    // Create formatted posts
    const twitterText = formatTwitterPost(userMessage, bubbeResponse);
    const blueskyText = formatBlueskyPost(userMessage, bubbeResponse);
    
    // Post to both platforms
    const results = {
        twitter: false,
        bluesky: false
    };
    
    // Post to Twitter
    results.twitter = await postToTwitter(twitterText);
    
    // Post to Bluesky  
    results.bluesky = await postToBluesky(blueskyText);
    
    // Log the results
    const logEntry = {
        timestamp: new Date().toISOString(),
        messageId,
        userId,
        userName,
        location,
        userSnippet: userMessage.substring(0, 50),
        bubbeSnippet: bubbeResponse.substring(0, 50),
        twitterPosted: results.twitter,
        blueskyPosted: results.bluesky,
        twitterText: results.twitter ? twitterText : null,
        blueskyText: results.bluesky ? blueskyText : null
    };
    
    const logPath = path.join(__dirname, '../logs/immediate-posts.json');
    let logs = [];
    try {
        logs = JSON.parse(await fs.readFile(logPath, 'utf8'));
    } catch (e) {
        // File doesn't exist
    }
    logs.push(logEntry);
    
    // Keep only last 100 entries
    if (logs.length > 100) {
        logs = logs.slice(-100);
    }
    
    await fs.writeFile(logPath, JSON.stringify(logs, null, 2));
    
    console.log(`\nResults:`);
    console.log(`Twitter: ${results.twitter ? '✅ POSTED' : '❌ FAILED'}`);
    console.log(`Bluesky: ${results.bluesky ? '✅ POSTED' : '❌ FAILED'}`);
    console.log(`=====================================\n`);
    
    return results;
}

module.exports = {
    handleImmediateSocialPost,
    formatTwitterPost,
    formatBlueskyPost,
    anonymizeText
};