← back to Dear Bubbe Nextjs

archived/social-posters/bubbe-twitter-smart.js

185 lines

#!/usr/bin/env node

const fs = require('fs').promises;
const path = require('path');
const axios = require('axios');
const { postTweet } = require('./twitter-v2-official');

// Configuration
const CONFIG = {
  MAX_DAILY_POSTS: 100,
  MIN_INTERVAL_SECONDS: 180, // 3 minutes
  STATS_FILE: '/root/Projects/dear-bubbe-admin/logs/twitter-stats.json',
  LAST_POST_FILE: '/root/Projects/dear-bubbe-admin/logs/last-twitter-post.json'
};

// Viral hashtags that work
const HASHTAGS = {
  always: ['#BubbeAI', '#Bubbe'],
  viral: ['#JewishTwitter', '#Dating', '#Relationships', '#MondayMotivation', '#Funny', '#Comedy', '#NoFilter', '#Truth', '#Savage', '#Life'],
  day: {
    0: '#SundayFunday',
    1: '#MondayMotivation', 
    2: '#TuesdayThoughts',
    3: '#WednesdayWisdom',
    4: '#ThursdayThoughts',
    5: '#FridayFeeling',
    6: '#SaturdayVibes'
  }
};

// Content templates
const CONTENT = [
  "My boyfriend only texts once a day",
  "Should I quit my job without another lined up?",
  "My mother-in-law criticized my cooking",
  "I spent $500 on shoes",
  "Is 35 too old to start dating?",
  "My husband forgot our anniversary",
  "Should I text my ex?",
  "I haven't been to temple in years",
  "My kids never call me",
  "My boss is micromanaging me"
];

// Get Bubbe response
async function getBubbeResponse(message) {
  try {
    const response = await axios.post('http://45.61.58.125:3011/api/chat', {
      message: message,
      mode: 'bubbe',
      userId: 'twitter-bot-' + Date.now()
    });
    return response.data.response;
  } catch (error) {
    console.error('Failed to get Bubbe response:', error.message);
    return null;
  }
}

// Format tweet
function formatTweet(response) {
  const day = new Date().getDay();
  const dayHashtag = HASHTAGS.day[day];
  const viralHashtag = HASHTAGS.viral[Math.floor(Math.random() * HASHTAGS.viral.length)];
  
  // Trim response to fit
  let trimmed = response;
  const maxLength = 280 - 50; // Leave room for hashtags
  if (trimmed.length > maxLength) {
    trimmed = trimmed.substring(0, maxLength - 3) + '...';
  }
  
  return `${trimmed} ${HASHTAGS.always.join(' ')} ${dayHashtag} ${viralHashtag}`;
}

// Check if can post
async function canPost() {
  try {
    // Check daily limit
    const stats = JSON.parse(await fs.readFile(CONFIG.STATS_FILE, 'utf8').catch(() => '{}'));
    const today = new Date().toDateString();
    
    if (stats.date !== today) {
      stats.date = today;
      stats.count = 0;
    }
    
    if (stats.count >= CONFIG.MAX_DAILY_POSTS) {
      console.log(`Daily limit reached: ${stats.count}/${CONFIG.MAX_DAILY_POSTS}`);
      return false;
    }
    
    // Check time since last post
    const lastPost = JSON.parse(await fs.readFile(CONFIG.LAST_POST_FILE, 'utf8').catch(() => '{}'));
    if (lastPost.time) {
      const elapsed = (Date.now() - lastPost.time) / 1000;
      if (elapsed < CONFIG.MIN_INTERVAL_SECONDS) {
        console.log(`Rate limit: wait ${Math.ceil(CONFIG.MIN_INTERVAL_SECONDS - elapsed)}s`);
        return false;
      }
    }
    
    return true;
  } catch (error) {
    console.error('Error checking limits:', error);
    return false;
  }
}

// Update stats
async function updateStats() {
  try {
    const stats = JSON.parse(await fs.readFile(CONFIG.STATS_FILE, 'utf8').catch(() => '{}'));
    const today = new Date().toDateString();
    
    if (stats.date !== today) {
      stats.date = today;
      stats.count = 0;
    }
    
    stats.count++;
    stats.total = (stats.total || 0) + 1;
    
    await fs.writeFile(CONFIG.STATS_FILE, JSON.stringify(stats, null, 2));
    await fs.writeFile(CONFIG.LAST_POST_FILE, JSON.stringify({ time: Date.now() }, null, 2));
    
    return stats;
  } catch (error) {
    console.error('Error updating stats:', error);
  }
}

// Post one tweet
async function postOne() {
  if (!await canPost()) {
    return false;
  }
  
  // Get random content
  const topic = CONTENT[Math.floor(Math.random() * CONTENT.length)];
  console.log('Topic:', topic);
  
  // Get Bubbe response
  const response = await getBubbeResponse(topic);
  if (!response) {
    console.log('No response from Bubbe');
    return false;
  }
  
  // Format and post
  const tweet = formatTweet(response);
  console.log('Tweet:', tweet.substring(0, 100) + '...');
  console.log('Length:', tweet.length);
  
  const result = await postTweet(tweet);
  
  if (result.success) {
    const stats = await updateStats();
    console.log(`✅ Posted! (${stats.count}/${CONFIG.MAX_DAILY_POSTS} today, ${stats.total} total)`);
    console.log(`URL: https://x.com/DearBubbe/status/${result.tweetId}`);
    return true;
  } else {
    console.log('❌ Failed:', result.error);
    return false;
  }
}

// Run continuously
async function run() {
  console.log('🚀 Bubbe Twitter Bot Started');
  console.log(`Posts per day: ${CONFIG.MAX_DAILY_POSTS}`);
  console.log(`Min interval: ${CONFIG.MIN_INTERVAL_SECONDS}s`);
  
  while (true) {
    await postOne();
    
    // Wait before next attempt
    await new Promise(r => setTimeout(r, CONFIG.MIN_INTERVAL_SECONDS * 1000));
  }
}

// Start
if (require.main === module) {
  run().catch(console.error);
}