← 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 };