Initial commit: config-driven Twitter bot with multi-prompt support

- Kernel.sh stealth browser for X login and posting
- Claude Sonnet 4.6 for tweet generation
- prompts.json for configurable prompts with frequency/scheduling
- Per-prompt history tracking to avoid repetition
- Scheduler with random time window support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt Jackson
2026-03-07 23:03:21 +00:00
commit 05baa8c573
6 changed files with 399 additions and 0 deletions

51
scheduler.js Normal file
View File

@@ -0,0 +1,51 @@
import { execFileSync } from "child_process";
import { readFileSync } from "fs";
import { fileURLToPath } from "url";
import { dirname, join } from "path";
const __dirname = dirname(fileURLToPath(import.meta.url));
const promptName = process.argv[2];
if (!promptName) {
console.error("Usage: node scheduler.js <prompt-name>");
process.exit(1);
}
const prompts = JSON.parse(readFileSync(join(__dirname, "prompts.json"), "utf-8"));
const config = prompts.find((p) => p.name === promptName);
if (!config) {
console.error(`Prompt "${promptName}" not found.`);
process.exit(1);
}
// Pick a random time in the window
const windowMinutes = (config.endHour - config.startHour) * 60;
const delayMinutes = Math.floor(Math.random() * windowMinutes);
const hours = Math.floor(delayMinutes / 60) + config.startHour;
const minutes = delayMinutes % 60;
const pad = (n) => String(n).padStart(2, "0");
console.log(`[${promptName}] Scheduled for ${pad(hours)}:${pad(minutes)} PST`);
const now = new Date(
new Date().toLocaleString("en-US", { timeZone: "America/Los_Angeles" })
);
const target = new Date(now);
target.setHours(hours, minutes, 0, 0);
let delayMs = target.getTime() - now.getTime();
if (delayMs <= 0) {
console.log("Target time passed. Posting now.");
delayMs = 0;
}
console.log(`Waiting ${Math.round(delayMs / 60000)} minutes...`);
setTimeout(() => {
try {
execFileSync("node", [join(__dirname, "bot.js"), promptName], { stdio: "inherit" });
} catch {
process.exit(1);
}
}, delayMs);