build-in-public-bot
Version:
AI-powered CLI bot for automating build-in-public tweets with code screenshots
282 lines ⢠14.5 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.initCommand = initCommand;
const logger_1 = require("../utils/logger");
const config_1 = require("../services/config");
const prompts_1 = require("../utils/prompts");
const chalk_1 = __importDefault(require("chalk"));
async function initCommand() {
logger_1.logger.info('Welcome to Build-in-Public Bot! đ\n');
const configService = config_1.ConfigService.getInstance();
try {
let existingConfig = null;
let isNewInstall = false;
try {
existingConfig = await configService.load();
const overwrite = await (0, prompts_1.confirm)('Configuration already exists. Do you want to update it?');
if (!overwrite) {
logger_1.logger.info('Using existing configuration.');
}
else {
isNewInstall = true;
}
}
catch {
isNewInstall = true;
}
let username = existingConfig?.twitter?.username;
if (isNewInstall) {
console.log(chalk_1.default.bold('\nđ Let\'s set up your build-in-public bot:\n'));
username = await (0, prompts_1.prompt)('Twitter username (without @)', {
default: username,
validate: (input) => {
if (!input)
return 'Username is required';
if (input.includes('@'))
return 'Please enter username without @ symbol';
return true;
}
});
}
else {
console.log(chalk_1.default.bold('\nđ§ Checking your setup:\n'));
console.log(chalk_1.default.green(`â
Twitter username: @${username}`));
}
let postingMethod = existingConfig?.twitter?.postingMethod;
if (isNewInstall || !postingMethod) {
console.log(chalk_1.default.bold('\nđŚ Twitter Posting Method:\n'));
console.log('This bot can post to Twitter in two ways:\n');
console.log(chalk_1.default.cyan('1. Browser Automation') + ' (Recommended)');
console.log(' - Uses Chrome to post like a human');
console.log(' - No API keys needed');
console.log(' - Works with personal accounts');
console.log(' - Handles media uploads easily\n');
console.log(chalk_1.default.cyan('2. Twitter API') + ' (Advanced)');
console.log(' - Requires Twitter Developer account');
console.log(' - Need to create an app and get API keys');
console.log(' - Faster but more complex setup');
console.log(' - Subject to API rate limits\n');
const methodChoice = await (0, prompts_1.prompt)('Which method would you like to use? (1 or 2)', {
validate: (input) => {
if (!['1', '2'].includes(input)) {
return 'Please enter 1 or 2';
}
return true;
}
});
postingMethod = methodChoice === '1' ? 'browser' : 'api';
}
else {
console.log(chalk_1.default.green(`â
Twitter posting method: ${postingMethod}`));
}
if (postingMethod === 'browser') {
if (isNewInstall || !existingConfig?.twitter?.postingMethod) {
console.log(chalk_1.default.green('\nâ
Great choice! Browser automation is easy to set up.'));
console.log(chalk_1.default.dim('When you post your first tweet, a Chrome window will open.'));
console.log(chalk_1.default.dim('Just log in to Twitter once, and the bot will remember your session.\n'));
}
}
else if (postingMethod === 'api') {
console.log(chalk_1.default.yellow('\nâ ď¸ Twitter API setup is more complex.'));
console.log(chalk_1.default.dim('\nYou\'ll need:'));
console.log(chalk_1.default.dim('1. A Twitter Developer account'));
console.log(chalk_1.default.dim('2. Create an app at https://developer.twitter.com'));
console.log(chalk_1.default.dim('3. Get your API keys and tokens\n'));
const setupApiNow = await (0, prompts_1.confirm)('Do you have Twitter API credentials ready?', false);
if (setupApiNow) {
console.log(chalk_1.default.dim('\nEnter your Twitter API credentials:\n'));
const apiKey = await (0, prompts_1.prompt)('API Key', {
mask: true,
validate: (input) => input.length > 0 || 'API Key is required'
});
const apiSecret = await (0, prompts_1.prompt)('API Key Secret', {
mask: true,
validate: (input) => input.length > 0 || 'API Key Secret is required'
});
const accessToken = await (0, prompts_1.prompt)('Access Token', {
mask: true,
validate: (input) => input.length > 0 || 'Access Token is required'
});
const accessSecret = await (0, prompts_1.prompt)('Access Token Secret', {
mask: true,
validate: (input) => input.length > 0 || 'Access Token Secret is required'
});
const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
const path = await Promise.resolve().then(() => __importStar(require('path')));
const envPath = path.join(process.cwd(), '.env');
try {
let envContent = '';
try {
envContent = await fs.readFile(envPath, 'utf-8');
}
catch {
}
const twitterEnvVars = `
TWITTER_API_KEY=${apiKey}
TWITTER_API_KEY_SECRET=${apiSecret}
TWITTER_ACCESS_TOKEN=${accessToken}
TWITTER_ACCESS_TOKEN_SECRET=${accessSecret}
`;
envContent += twitterEnvVars;
await fs.writeFile(envPath, envContent);
process.env.TWITTER_API_KEY = apiKey;
process.env.TWITTER_API_KEY_SECRET = apiSecret;
process.env.TWITTER_ACCESS_TOKEN = accessToken;
process.env.TWITTER_ACCESS_TOKEN_SECRET = accessSecret;
console.log(chalk_1.default.green('\nâ
Twitter API credentials saved!'));
}
catch (error) {
console.log(chalk_1.default.yellow('\nâ ď¸ Could not save credentials to .env'));
console.log(chalk_1.default.dim('Add these to your .env file manually:'));
console.log(chalk_1.default.cyan(`TWITTER_API_KEY=${apiKey}`));
console.log(chalk_1.default.cyan(`TWITTER_API_KEY_SECRET=${apiSecret}`));
console.log(chalk_1.default.cyan(`TWITTER_ACCESS_TOKEN=${accessToken}`));
console.log(chalk_1.default.cyan(`TWITTER_ACCESS_TOKEN_SECRET=${accessSecret}`));
}
const config = await configService.load();
config.twitter.postingMethod = 'api';
await configService.save(config);
}
else {
console.log(chalk_1.default.dim('\nYou can set up Twitter API credentials later.'));
console.log(chalk_1.default.dim('For now, the bot will use browser automation.\n'));
const config = await configService.load();
config.twitter.postingMethod = 'browser';
await configService.save(config);
}
}
console.log(chalk_1.default.bold('\nđ¤ AI Configuration:\n'));
console.log('This bot uses OpenRouter with GPT-4 for generating tweets.');
if (!process.env.OPENROUTER_API_KEY) {
console.log(chalk_1.default.yellow('\nâ ď¸ No OpenRouter API key detected'));
const setupApiKey = await (0, prompts_1.confirm)('Would you like to set up your API key now?', true);
if (setupApiKey) {
console.log(chalk_1.default.dim('\n1. Sign up at https://openrouter.ai (free)'));
console.log(chalk_1.default.dim('2. Copy your API key from the dashboard\n'));
const apiKey = await (0, prompts_1.prompt)('Paste your OpenRouter API key', {
mask: true,
validate: (input) => {
if (!input)
return 'API key is required';
if (input.length < 20)
return 'Invalid API key format';
return true;
}
});
const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
const path = await Promise.resolve().then(() => __importStar(require('path')));
const envPath = path.join(process.cwd(), '.env');
try {
let envContent = '';
try {
envContent = await fs.readFile(envPath, 'utf-8');
}
catch {
}
if (envContent.includes('OPENROUTER_API_KEY')) {
envContent = envContent.replace(/OPENROUTER_API_KEY=.*/, `OPENROUTER_API_KEY=${apiKey}`);
}
else {
envContent += `${envContent ? '\n' : ''}OPENROUTER_API_KEY=${apiKey}\n`;
}
await fs.writeFile(envPath, envContent);
process.env.OPENROUTER_API_KEY = apiKey;
console.log(chalk_1.default.green('â
API key saved to .env file'));
}
catch (error) {
console.log(chalk_1.default.yellow('â ď¸ Could not save to .env file'));
console.log(chalk_1.default.dim('Add this to your environment manually:'));
console.log(chalk_1.default.cyan(`export OPENROUTER_API_KEY="${apiKey}"`));
}
}
else {
console.log(chalk_1.default.dim('\nYou can add it later to your .env file:'));
console.log(chalk_1.default.cyan('OPENROUTER_API_KEY=your-key-here'));
}
}
else {
console.log(chalk_1.default.green('â
API key already configured'));
}
let useEmojis = true;
let alwaysUseBuildinpublic = true;
if (isNewInstall) {
console.log(chalk_1.default.bold('\n⨠Style Preferences:\n'));
useEmojis = await (0, prompts_1.confirm)('Do you want to use emojis in your tweets?', true);
alwaysUseBuildinpublic = await (0, prompts_1.confirm)('Always include #buildinpublic hashtag?', true);
}
if (isNewInstall) {
await configService.init();
}
const config = await configService.load();
if (username)
config.twitter.username = username;
if (postingMethod)
config.twitter.postingMethod = postingMethod;
if (isNewInstall) {
if (!useEmojis) {
config.style.emojis.frequency = 'none';
}
if (!alwaysUseBuildinpublic) {
config.style.hashtags.always = [];
}
}
await configService.save(config);
console.log(chalk_1.default.green('\nâ
Build-in-Public Bot setup complete!\n'));
console.log(chalk_1.default.bold('Current Configuration:'));
console.log(` Twitter: @${config.twitter.username} (${config.twitter.postingMethod} mode)`);
console.log(` AI Key: ${process.env.OPENROUTER_API_KEY ? chalk_1.default.green('â Configured') : chalk_1.default.yellow('â Not set')}`);
console.log(` Style: ${config.style.tone} tone, ${config.style.emojis.frequency} emojis`);
console.log('\n' + chalk_1.default.bold('Next steps:'));
if (!process.env.OPENROUTER_API_KEY) {
console.log(' 1. Set up your API key:', chalk_1.default.cyan('bip setup-api'));
console.log(' 2. Post your first tweet:', chalk_1.default.cyan('bip post "your update"'));
}
else {
console.log(' 1. Post your first tweet:', chalk_1.default.cyan('bip post "your update"'));
console.log(' 2. Share code screenshots:', chalk_1.default.cyan('bip code <file>'));
}
console.log(' 3. Customize your style:', chalk_1.default.cyan('bip style'));
console.log(' 4. See all commands:', chalk_1.default.cyan('bip --help'));
}
catch (error) {
logger_1.logger.error('Failed to initialize configuration');
throw error;
}
}
//# sourceMappingURL=init.js.map
;