Skip to content

Error: createTweet 403 #97

Open
Open
@jiaohu

Description

When I use sdk to post twitter with code below and I get 403 error. How could I use it correctly?

export class TwitterHandler {
    authClient: auth.OAuth2User;
    client: Client;
    agent: AgentClient;

    constructor() {
        this.authClient = new auth.OAuth2User({
            client_id: envConfig.X_CLIENT_ID,
            client_secret: envConfig.X_CLIENT_SECRET,
            callback: `${envConfig.SERVER_DOMAIN}/callback`,
            scopes: ["tweet.read", "users.read", "tweet.write", "offline.access"],
        });
        this.client = new Client(this.authClient);
        this.agent = new OpenAIAgent();
    }


    async checkAndUpdateAccessToken() : Promise<void> {
        if (this.authClient.isAccessTokenExpired()) {
            await this.authClient.refreshAccessToken()
        }
    }

    async sendTweet(text: string): Promise<void> {
        try {
            let result = await this.agent.responseText(text);
            if (!result) {
                throw new Error("Processed tweet text cannot be null or empty");
            }

            await this.checkAndUpdateAccessToken();
            console.log("result", result);
            console.log("authClient", this.authClient)
            await this.client.tweets.createTweet({ text: result })
        } catch (error) {
            console.error("Error posting tweet:", error);
        }
    }
}
app.get('/auth', (req, res) => {
    const authUrl = twitterHandler.authClient.generateAuthURL({
        state: 'state',
        code_challenge_method: 'plain',
        code_challenge: 'challenge',
    });
    res.redirect(authUrl);
});

app.get('/callback', async (req, res) => {
    const code = req.query.code as string;
    console.log("before", twitterHandler.authClient)
    let token = await twitterHandler.authClient.requestAccessToken(code);

    const userInfo = await twitterHandler.getAuthenticatedUserInfo();
    console.log(token.token)
    await twitterHandler.sendTweet("hello")
    if (userInfo) {
        res.send(`Authentication successful! User ID: ${userInfo.id}, User Name: ${userInfo.name}`);
    } else {
        res.status(500).send("Error fetching user info");
    }
});

Expected behavior

When the twitter user call /auth in the browser, and authorize to the application, then It will post a tweet.

Actual behavior

get error with code 403, more detail in the below


2025-01-10 15:48:00 | {"log":"before OAuth2User { token: undefined }\n","stream":"stdout","time":"2025-01-10T07:48:00.151143423Z"}
-- | --
  | 2025-01-10 15:48:00 | {"log":"{\n","stream":"stdout","time":"2025-01-10T07:48:00.548233539Z"}
  | 2025-01-10 15:48:00 | {"log":"  token_type: 'bearer',\n","stream":"stdout","time":"2025-01-10T07:48:00.548246461Z"}
  | 2025-01-10 15:48:00 | {"log":"  access_token: 'QVdsT29FM*****OjE',\n","stream":"stdout","time":"2025-01-10T07:48:00.548249972Z"}
  | 2025-01-10 15:48:00 | {"log":"  scope: 'tweet.write users.read tweet.read offline.access',\n","stream":"stdout","time":"2025-01-10T07:48:00.548253294Z"}
  | 2025-01-10 15:48:00 | {"log":"  refresh_token: 'MHZTMXNvcC1NS****OnJ0OjE',\n","stream":"stdout","time":"2025-01-10T07:48:00.548256494Z"}
  | 2025-01-10 15:48:00 | {"log":"  expires_at: 1736502480492\n","stream":"stdout","time":"2025-01-10T07:48:00.548259962Z"}
  | 2025-01-10 15:48:00 | {"log":"}\n","stream":"stdout","time":"2025-01-10T07:48:00.548262902Z"}
  | 2025-01-10 15:48:02 | {"log":"result Why did *****🐔💰 \n","stream":"stdout","time":"2025-01-10T07:48:02.195043921Z"}
  | 2025-01-10 15:48:02 | {"log":"\n","stream":"stdout","time":"2025-01-10T07:48:02.195083118Z"}
  | 2025-01-10 15:48:02 | {"log":"But seriously, my dear influencer friend******n! 🐓🚀 \n","stream":"stdout","time":"2025-01-10T07:48:02.195087814Z"}
  | 2025-01-10 15:48:02 | {"log":"\n","stream":"stdout","time":"2025-01-10T07:48:02.195091444Z"}
  | 2025-01-10 15:48:02 | {"log":"For today's ******","stream":"stdout","time":"2025-01-10T07:48:02.195094169Z"}
  | 2025-01-10 15:48:02 | {"log":"authClient OAuth2User {\n","stream":"stdout","time":"2025-01-10T07:48:02.19527886Z"}
  | 2025-01-10 15:48:02 | {"log":"  token: {\n","stream":"stdout","time":"2025-01-10T07:48:02.19528524Z"}
  | 2025-01-10 15:48:02 | {"log":"    token_type: 'bearer',\n","stream":"stdout","time":"2025-01-10T07:48:02.195288325Z"}
  | 2025-01-10 15:48:02 | {"log":"    access_token: 'QVdsT29FMk9****TowOmF0OjE',\n","stream":"stdout","time":"2025-01-10T07:48:02.195291498Z"}
  | 2025-01-10 15:48:02 | {"log":"    scope: 'tweet.write users.read tweet.read offline.access',\n","stream":"stdout","time":"2025-01-10T07:48:02.195295135Z"}
  | 2025-01-10 15:48:02 | {"log":"    refresh_token: 'MHZTMXNvcC1NSnI0****ODA0ODA6MTowOnJ0OjE',\n","stream":"stdout","time":"2025-01-10T07:48:02.195298292Z"}
  | 2025-01-10 15:48:02 | {"log":"    expires_at: 1736502480492\n","stream":"stdout","time":"2025-01-10T07:48:02.195301608Z"}
  | 2025-01-10 15:48:02 | {"log":"  }\n","stream":"stdout","time":"2025-01-10T07:48:02.195331158Z"}
  | 2025-01-10 15:48:02 | {"log":"}\n","stream":"stdout","time":"2025-01-10T07:48:02.19533885Z"}
  | 2025-01-10 15:48:02 | {"log":"Error posting tweet: TwitterResponseError\n","stream":"stderr","time":"2025-01-10T07:48:02.273021542Z"}
  | 2025-01-10 15:48:02 | {"log":"    at request (/app/node_modules/twitter-api-sdk/dist/request.js:67:15)\n","stream":"stderr","time":"2025-01-10T07:48:02.273033255Z"}
  | 2025-01-10 15:48:02 | {"log":"    at processTicksAndRejections (node:internal/process/task_queues:105:5)\n","stream":"stderr","time":"2025-01-10T07:48:02.273035931Z"}
  | 2025-01-10 15:48:02 | {"log":"    at async rest (/app/node_modules/twitter-api-sdk/dist/request.js:100:22) {\n","stream":"stderr","time":"2025-01-10T07:48:02.273038241Z"}
  | 2025-01-10 15:48:02 | {"log":"  status: 403,\n","stream":"stderr","time":"2025-01-10T07:48:02.273040415Z"}
  | 2025-01-10 15:48:02 | {"log":"  statusText: 'Forbidden',\n","stream":"stderr","time":"2025-01-10T07:48:02.273042335Z"}
  | 2025-01-10 15:48:02 | {"log":"  headers: {\n","stream":"stderr","time":"2025-01-10T07:48:02.273044301Z"}
  | 2025-01-10 15:48:02 | {"log":"    'api-version': '2.120',\n","stream":"stderr","time":"2025-01-10T07:48:02.273046199Z"}
  | 2025-01-10 15:48:02 | {"log":"    'cache-control': 'no-cache, no-store, max-age=0',\n","stream":"stderr","time":"2025-01-10T07:48:02.273048098Z"}
  | 2025-01-10 15:48:02 | {"log":"    'content-disposition': 'attachment; filename=json.json',\n","stream":"stderr","time":"2025-01-10T07:48:02.273050001Z"}
  | 2025-01-10 15:48:02 | {"log":"    'content-encoding': 'gzip',\n","stream":"stderr","time":"2025-01-10T07:48:02.273051938Z"}
  | 2025-01-10 15:48:02 | {"log":"    'content-length': '122',\n","stream":"stderr","time":"2025-01-10T07:48:02.273053981Z"}
  | 2025-01-10 15:48:02 | {"log":"    'content-type': 'application/json; charset=utf-8',\n","stream":"stderr","time":"2025-01-10T07:48:02.273055835Z"}
  | 2025-01-10 15:48:02 | {"log":"    date: 'Fri, 10 Jan 2025 07:48:02 UTC',\n","stream":"stderr","time":"2025-01-10T07:48:02.273057724Z"}
  | 2025-01-10 15:48:02 | {"log":"    perf: '7402827104',\n","stream":"stderr","time":"2025-01-10T07:48:02.273059634Z"}
  | 2025-01-10 15:48:02 | {"log":"    server: 'tsa_p',\n","stream":"stderr","time":"2025-01-10T07:48:02.273061819Z"}
  | 2025-01-10 15:48:02 | {"log":"    'set-cookie': 'guest_id=v1%3A173649528220742917; Max-Age=34214400; Expires=Tue, 10 Feb 2026 07:48:02 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None',\n","stream":"stderr","time":"2025-01-10T07:48:02.273063748Z"}
  | 2025-01-10 15:48:02 | {"log":"    'strict-transport-security': 'max-age=631138519',\n","stream":"stderr","time":"2025-01-10T07:48:02.273065837Z"}
  | 2025-01-10 15:48:02 | {"log":"    'x-access-level': 'read-write',\n","stream":"stderr","time":"2025-01-10T07:48:02.27306772Z"}
  | 2025-01-10 15:48:02 | {"log":"    'x-app-limit-24hour-limit': '17',\n","stream":"stderr","time":"2025-01-10T07:48:02.273069891Z"}
  | 2025-01-10 15:48:02 | {"log":"    'x-app-limit-24hour-remaining': '16',\n","stream":"stderr","time":"2025-01-10T07:48:02.273072615Z"}
  | 2025-01-10 15:48:02 | {"log":"    'x-app-limit-24hour-reset': '1736581682',\n","stream":"stderr","time":"2025-01-10T07:48:02.27307566Z"}
  | 2025-01-10 15:48:02 | {"log":"    'x-connection-hash': 'b9dbb429a720a831a3f3a37ffe3352ea6926a45e8eff752b7e4298ec4292afe5',\n","stream":"stderr","time":"2025-01-10T07:48:02.2730786Z"}
  | 2025-01-10 15:48:02 | {"log":"    'x-content-type-options': 'nosniff',\n","stream":"stderr","time":"2025-01-10T07:48:02.273081951Z"}
  | 2025-01-10 15:48:02 | {"log":"    'x-frame-options': 'SAMEORIGIN',\n","stream":"stderr","time":"2025-01-10T07:48:02.273085641Z"}
  | 2025-01-10 15:48:02 | {"log":"    'x-rate-limit-limit': '1080000',\n","stream":"stderr","time":"2025-01-10T07:48:02.273087799Z"}
  | 2025-01-10 15:48:02 | {"log":"    'x-rate-limit-remaining': '1079999',\n","stream":"stderr","time":"2025-01-10T07:48:02.273089615Z"}
  | 2025-01-10 15:48:02 | {"log":"    'x-rate-limit-reset': '1736496182',\n","stream":"stderr","time":"2025-01-10T07:48:02.273091455Z"}
  | 2025-01-10 15:48:02 | {"log":"    'x-response-time': '61',\n","stream":"stderr","time":"2025-01-10T07:48:02.273093328Z"}
  | 2025-01-10 15:48:02 | {"log":"    'x-transaction-id': 'b018055ffeca5a70',\n","stream":"stderr","time":"2025-01-10T07:48:02.273095182Z"}
  | 2025-01-10 15:48:02 | {"log":"    'x-user-limit-24hour-limit': '17',\n","stream":"stderr","time":"2025-01-10T07:48:02.273097111Z"}
  | 2025-01-10 15:48:02 | {"log":"    'x-user-limit-24hour-remaining': '16',\n","stream":"stderr","time":"2025-01-10T07:48:02.273098975Z"}
  | 2025-01-10 15:48:02 | {"log":"    'x-user-limit-24hour-reset': '1736581682',\n","stream":"stderr","time":"2025-01-10T07:48:02.273100845Z"}
  | 2025-01-10 15:48:02 | {"log":"    'x-xss-protection': '0'\n","stream":"stderr","time":"2025-01-10T07:48:02.273102743Z"}
  | 2025-01-10 15:48:02 | {"log":"  },\n","stream":"stderr","time":"2025-01-10T07:48:02.273113161Z"}
  | 2025-01-10 15:48:02 | {"log":"  error: {\n","stream":"stderr","time":"2025-01-10T07:48:02.27311512Z"}
  | 2025-01-10 15:48:02 | {"log":"    detail: 'You are not permitted to perform this action.',\n","stream":"stderr","time":"2025-01-10T07:48:02.273116957Z"}
  | 2025-01-10 15:48:02 | {"log":"    type: 'about:blank',\n","stream":"stderr","time":"2025-01-10T07:48:02.273118855Z"}
  | 2025-01-10 15:48:02 | {"log":"    title: 'Forbidden',\n","stream":"stderr","time":"2025-01-10T07:48:02.273120724Z"}
  | 2025-01-10 15:48:02 | {"log":"    status: 403\n","stream":"stderr","time":"2025-01-10T07:48:02.273122578Z"}
  | 2025-01-10 15:48:02 | {"log":"  }\n","stream":"stderr","time":"2025-01-10T07:48:02.273124428Z"}
  | 2025-01-10 15:48:02 | {"log":"}\n","stream":"stderr","time":"2025-01-10T07:48:02.273126205Z"}


Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions