← back to Dear Bubbe Nextjs

archived/social-posters/twitter-anonymous-simple.js

191 lines

#!/usr/bin/env node

const fs = require('fs').promises;
const path = require('path');
const axios = require('axios');
const { exec } = require('child_process');
const util = require('util');
const execPromise = util.promisify(exec);

// Import anonymization
const { anonymizeMessage } = require('./anonymous-user-poster');

// Configuration
const CONFIG = {
  POSTS_PER_DAY: 20,
  MIN_INTERVAL_MINUTES: 30,
  STATE_FILE: '/root/Projects/dear-bubbe-admin/logs/twitter-anonymous-state.json',
  POSTED_FILE: '/root/Projects/dear-bubbe-admin/logs/twitter-posted.txt',
  QUEUE_FILE: '/root/Projects/dear-bubbe-admin/logs/twitter-queue.json'
};

// Get user messages
async function getUserMessages() {
  try {
    const { stdout } = await execPromise(
      `pm2 logs bubbe-ai --lines 1000 --nostream | grep -E "message:" | grep -v "__init__" | grep -v "__returning" | grep -v "favicon" | head -50`
    );
    
    const messages = [];
    const lines = stdout.split('\n').filter(line => line.trim());
    
    for (const line of lines) {
      const match = line.match(/message:\s*["'](.+?)["']/);
      if (match && match[1]) {
        const msg = match[1].trim();
        if (msg.length > 20 && !msg.includes('__') && !msg.startsWith('test')) {
          messages.push(msg);
        }
      }
    }
    
    return [...new Set(messages)];
  } catch (error) {
    console.error('Error getting messages:', error);
    return [];
  }
}

// Format for Twitter
function formatTweet(userMessage) {
  const intros = [
    "Real user question:",
    "Someone asked Bubbe:",
    "Today's dilemma:",
    "Anonymous asks:",
    "User needs advice:"
  ];
  
  const intro = intros[Math.floor(Math.random() * intros.length)];
  const anonymized = anonymizeMessage(userMessage);
  
  // Keep it short for Twitter
  const maxLength = 180;
  const truncated = anonymized.length > maxLength ? 
    anonymized.substring(0, maxLength) + '...' : 
    anonymized;
  
  return `${intro}\n"${truncated}"\n\n#BubbeAI #Advice\nbubbe.ai`;
}

// Save to queue for manual posting
async function saveToQueue(tweets) {
  try {
    await fs.writeFile(CONFIG.QUEUE_FILE, JSON.stringify(tweets, null, 2));
    console.log(`📝 Saved ${tweets.length} tweets to queue`);
  } catch (error) {
    console.error('Error saving queue:', error);
  }
}

// Main function
async function generateAnonymousTweets() {
  console.log('🐦 Twitter Anonymous Content Generator');
  console.log('=====================================');
  
  // Get messages
  const messages = await getUserMessages();
  console.log(`📝 Found ${messages.length} user messages`);
  
  // Load posted messages
  let postedMessages = [];
  try {
    const posted = await fs.readFile(CONFIG.POSTED_FILE, 'utf8');
    postedMessages = posted.split('\n').filter(m => m.trim());
  } catch {}
  
  // Filter unposted
  const unposted = messages.filter(msg => !postedMessages.includes(msg));
  console.log(`📌 ${unposted.length} new messages available`);
  
  if (unposted.length === 0) {
    console.log('❌ No new messages');
    return;
  }
  
  // Generate tweets for next batch
  const tweets = [];
  const toPost = unposted.slice(0, 10); // Prepare 10 tweets
  
  for (const message of toPost) {
    const tweet = formatTweet(message);
    tweets.push({
      original: message,
      tweet: tweet,
      timestamp: new Date().toISOString()
    });
  }
  
  // Save queue
  await saveToQueue(tweets);
  
  // Display for manual posting
  console.log('\n📋 TWEETS READY FOR POSTING:');
  console.log('============================\n');
  
  tweets.forEach((t, i) => {
    console.log(`Tweet ${i + 1}:`);
    console.log('-'.repeat(40));
    console.log(t.tweet);
    console.log('-'.repeat(40) + '\n');
  });
  
  console.log('TO POST:');
  console.log('1. Go to https://x.com/compose/post');
  console.log('2. Copy and paste each tweet');
  console.log('3. Schedule or post immediately');
  console.log('\nAfter posting, run:');
  console.log('node /root/Projects/dear-bubbe-nextjs/lib/mark-twitter-posted.js');
  
  // Also try automated posting
  console.log('\n🤖 Attempting automated post...');
  await tryAutomatedPost(tweets[0]);
}

// Try automated posting using curl
async function tryAutomatedPost(tweetData) {
  try {
    // Create a simple webhook that could be used with IFTTT or Zapier
    const webhookData = {
      text: tweetData.tweet,
      timestamp: tweetData.timestamp,
      platform: 'twitter'
    };
    
    // Save for external tools
    const webhookFile = '/root/Projects/dear-bubbe-admin/public/latest-tweet.json';
    await fs.writeFile(webhookFile, JSON.stringify(webhookData, null, 2));
    console.log('✅ Tweet data saved for webhooks');
    
    // Also save as text file for easy copying
    const textFile = '/root/Projects/dear-bubbe-admin/public/tweet.txt';
    await fs.writeFile(textFile, tweetData.tweet);
    console.log('✅ Tweet saved as text file');
    
    console.log('\n📱 Access tweet at:');
    console.log('http://45.61.58.125:7902/latest-tweet.json');
    console.log('http://45.61.58.125:7902/tweet.txt');
    
  } catch (error) {
    console.error('Error in automated post:', error);
  }
}

// Mark messages as posted
async function markAsPosted(messages) {
  try {
    const posted = await fs.readFile(CONFIG.POSTED_FILE, 'utf8').catch(() => '');
    const updated = posted + '\n' + messages.join('\n');
    await fs.writeFile(CONFIG.POSTED_FILE, updated);
    console.log(`✅ Marked ${messages.length} messages as posted`);
  } catch (error) {
    console.error('Error marking as posted:', error);
  }
}

// Run
if (require.main === module) {
  generateAnonymousTweets().catch(console.error);
}

module.exports = { generateAnonymousTweets };