File size: 2,648 Bytes
4aac4d3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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");
});