← back to Dear Bubbe Nextjs

lib/twitter-v2-official.js

111 lines

const { TwitterApi } = require('twitter-api-v2');
const fs = require('fs').promises;

// Load config from saved file
const config = require('./twitter-api-config.json');

async function postTweet(text) {
  console.log('🐦 Posting to Twitter/X via Official API v2...');
  console.log('Tweet:', text.substring(0, 80) + '...\n');
  
  try {
    const client = new TwitterApi({
      appKey: config.appKey,
      appSecret: config.appSecret,
      accessToken: config.accessToken,
      accessSecret: config.accessSecret,
    });
    
    const tweet = await client.v2.tweet(text);
    console.log('✅ Tweet posted successfully!');
    console.log('Tweet ID:', tweet.data.id);
    console.log('URL: https://x.com/DearBubbe/status/' + tweet.data.id);
    return { success: true, tweetId: tweet.data.id };
  } catch (error) {
    console.error('❌ Error posting tweet:', error.message);
    if (error.data) {
      console.error('API Error:', JSON.stringify(error.data, null, 2));
    }
    return { success: false, error: error.message };
  }
}

async function postFromQueue() {
  try {
    const queuePath = '/root/Projects/dear-bubbe-admin/logs/twitter-queue.json';
    const queue = JSON.parse(await fs.readFile(queuePath, 'utf8'));
    
    const toPost = queue.find(t => t.status === 'queued');
    if (!toPost) {
      console.log('No posts in queue');
      return false;
    }
    
    console.log('📤 Posting from queue...');
    const result = await postTweet(toPost.text);
    
    if (result.success) {
      toPost.status = 'posted';
      toPost.postedAt = new Date().toISOString();
      toPost.tweetId = result.tweetId;
      await fs.writeFile(queuePath, JSON.stringify(queue, null, 2));
      console.log('✅ Queue updated');
      
      // Log to immediate posts
      const immediateLogsPath = '/root/Projects/dear-bubbe-admin/logs/immediate-posts.json';
      try {
        const logs = JSON.parse(await fs.readFile(immediateLogsPath, 'utf8'));
        logs.push({
          timestamp: new Date().toISOString(),
          platform: 'twitter',
          status: 'posted',
          tweetId: result.tweetId,
          text: toPost.text,
          url: `https://x.com/DearBubbe/status/${result.tweetId}`
        });
        await fs.writeFile(immediateLogsPath, JSON.stringify(logs, null, 2));
      } catch (e) {
        // Log file might not exist
      }
    }
    
    return result.success;
  } catch (error) {
    console.error('Error:', error);
    return false;
  }
}

async function postAll() {
  console.log('📬 Posting all queued tweets...\n');
  let posted = 0;
  
  while (true) {
    const success = await postFromQueue();
    if (!success) break;
    posted++;
    
    // Wait 5 seconds between posts to avoid rate limits
    if (posted < 10) {
      console.log('Waiting 5 seconds before next post...\n');
      await new Promise(r => setTimeout(r, 5000));
    } else {
      break;
    }
  }
  
  console.log(`\n✅ Posted ${posted} tweets total`);
  return posted;
}

module.exports = { postTweet, postFromQueue, postAll };

if (require.main === module) {
  if (process.argv[2] === 'test') {
    postTweet('Testing Bubbe with official API! The schmucks finally got it working! #BubbeAI Visit Bubbe.AI');
  } else if (process.argv[2] === 'all') {
    postAll();
  } else {
    postFromQueue();
  }
}