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