Skip to content

feat(adapter-twitter): add new Twitter/X webhook adapter#275

Open
JimiPatel2023 wants to merge 1 commit intovercel:mainfrom
JimiPatel2023:feature/twitter-adapter
Open

feat(adapter-twitter): add new Twitter/X webhook adapter#275
JimiPatel2023 wants to merge 1 commit intovercel:mainfrom
JimiPatel2023:feature/twitter-adapter

Conversation

@JimiPatel2023
Copy link

Description

This PR introduces a brand new, fully-featured adapter for Twitter / X.

It implements the Adapter interface to handle Direct Messages via the Enterprise Account Activity API (webhooks) and the v2 REST API to reply to users.

Features

  • Fully implemented Adapter interface matching existing Vercel adapters.
  • Handles the mandatory CRC (Challenge-Response Check) verification.
  • Message parsing with bot identification & mention deduction.
  • Uses TwitterFormatConverter to convert AST to plain text / ASCII tables since Twitter DMs don't support rich markdown formatting.
  • Comprehensive test suite (38 tests) with 100% pass rate.

Related Changes

  • Added @chat-adapter/twitter to vitest workspaces.
  • Documented in adapters.mdx feature matrix.
  • Added sample-messages.md and complete configuration README.md.
  • Included changeset for the release pipeline.

@vercel
Copy link
Contributor

vercel bot commented Mar 20, 2026

@JimiPatel2023 is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.


// For POST requests, verify the webhook signature
const body = await request.text();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing webhook signature verification on POST requests allows forged payloads to trigger arbitrary bot actions.

Fix on Vercel

Copy link

@claycurry claycurry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

only commenting bc @JimiPatel2023 and I independently encountered this issue at similar times, and spent time quietly deep diving our solutions in parallel. i left my fork online to compare solutions. my comments are my bookkeeping that we checked mostly the same conditions. it was neat

i left some nitpicks in the README, but overall its LGTM.

2. Generate your **Consumer Key**, **Consumer Secret**, and **Bearer Token**.
3. Set up **OAuth 1.0a User Authentication** in your app settings with Read/Write/Direct Messages permissions.
4. Generate the **Access Token** and **Access Token Secret** for your bot account.
5. Apply for the **Account Activity API** (requires Pro or Enterprise access).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Step 5 required more research for me to understand. Some tweaks to help the next fella:

  1. "Apply for..." – cant find the application – can you attach a link?

  2. "Account Activity API" – specify version (Enterprise or V2)?

});
});

describe("thread ID encoding/decoding", () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

});
});

describe("renderFormatted", () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

});
});

describe("editMessage", () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

});

describe("fetchThread", () => {
it("should return thread info", async () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

describe("fetchMessages", () => {
it("should return empty array for unknown thread", async () => {
const adapter = createTestAdapter();
const result = await adapter.fetchMessages("twitter:unknown");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

});

describe("parseMessage", () => {
it("should parse a raw DM event into a Message", () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

});
});

describe("isDM", () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants