Spaces:
Paused
Paused
| import express from "express"; | |
| import puppeteer from "puppeteer"; | |
| import fs from "fs"; | |
| import path from "path"; | |
| const app = express(); | |
| app.use(express.json()); | |
| const cookiesPath = path.resolve("./cookies.json"); | |
| app.post("/tweet", async (req, res) => { | |
| const { tweetText, imagePath, username, password } = req.body; | |
| if (!tweetText || !imagePath) { | |
| return res.status(400).json({ error: "Missing parameters" }); | |
| } | |
| const browser = await puppeteer.launch({ | |
| headless: true, | |
| args: ["--no-sandbox", "--disable-setuid-sandbox"], | |
| }); | |
| try { | |
| const page = await browser.newPage(); | |
| if (fs.existsSync(cookiesPath)) { | |
| const cookiesString = fs.readFileSync(cookiesPath, "utf8"); | |
| const cookies = JSON.parse(cookiesString); | |
| await page.setCookie(...cookies); | |
| console.log("β cookies loaded"); | |
| } | |
| await page.goto("https://twitter.com/home", { waitUntil: "networkidle2" }); | |
| if (await page.$('input[name="text"]')) { | |
| if (!username || !password) { | |
| return res.status(403).json({ error: "cookies expired, please provide username/password" }); | |
| } | |
| console.log("π need login"); | |
| await page.type('input[name="text"]', username, {delay: 100}); | |
| await page.keyboard.press('Enter'); | |
| await page.waitForTimeout(2000); | |
| await page.waitForSelector('input[name="password"]', {timeout: 60000}); | |
| await page.type('input[name="password"]', password, {delay: 100}); | |
| await page.keyboard.press('Enter'); | |
| await page.waitForNavigation({waitUntil: 'networkidle2'}); | |
| const cookies = await page.cookies(); | |
| fs.writeFileSync(cookiesPath, JSON.stringify(cookies, null, 2)); | |
| console.log("β cookies saved"); | |
| } | |
| await page.waitForSelector('a[aria-label="Post"]', { timeout: 60000 }); | |
| await page.click('a[aria-label="Post"]'); | |
| await page.waitForSelector('div[aria-label="Tweet text"]'); | |
| await page.type('div[aria-label="Tweet text"]', tweetText); | |
| const [fileChooser] = await Promise.all([ | |
| page.waitForFileChooser(), | |
| page.click('div[aria-label="Add photos or video"]') | |
| ]); | |
| await fileChooser.accept([imagePath]); | |
| await page.waitForTimeout(3000); | |
| await page.click('div[data-testid="tweetButtonInline"]'); | |
| await browser.close(); | |
| res.json({ success: true, message: "Tweet posted" }); | |
| } catch (err) { | |
| console.error(err); | |
| await browser.close(); | |
| res.status(500).json({ error: err.message }); | |
| } | |
| }); | |
| app.get("/", (_, res) => { | |
| res.send("β MCP Puppeteer Twitter server is running"); | |
| }); | |
| app.listen(7860, () => { | |
| console.log("β MCP server on port 7860"); | |
| }); | |