#
tokens: 8839/50000 9/9 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .gitignore
├── LICENSE
├── package.json
├── pnpm-lock.yaml
├── README.md
├── src
│   ├── index.ts
│   ├── interfaces
│   │   └── index.ts
│   ├── schema
│   │   └── index.ts
│   ├── tools
│   │   └── twitter.ts
│   └── types
│       └── index.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# compiled output
**/build
**/node_modules
**/.turbo
.env

# Client
/client/.next
/client/node_modules
/client/dist
/client/.env.local

# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# OS
.DS_Store

# Tests
/coverage
/.nyc_output

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

# TS config
*.tsbuildinfo
tsconfig.build.tsbuildinfo
tsconfig.tsbuildinfo

*.tgz

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# MCP Twitter

## Description
MCP Twitter is a server based on the Model Context Protocol that allows direct interaction with Twitter/X. It exposes various Twitter API functionalities through a standardized set of tools, enabling AI models and applications to perform actions on Twitter.

## Features

This MCP server provides the following actions:

- **`create_twitter_post`**: Create a new X/Twitter post
- **`reply_twitter_tweet`**: Reply to a specific X/Twitter post by ID
- **`get_last_tweet`**: Get the most recent post from a specified X/Twitter account
- **`get_last_tweets_options`**: Get a specified number of posts matching a search query
- **`create_and_post_twitter_thread`**: Create and publish an X/Twitter thread
- **`follow_twitter_from_username`**: Follow an X/Twitter user by username
- **`get_twitter_profile_from_username`**: Get complete X/Twitter profile data by username
- **`get_twitter_user_id_from_username`**: Get X/Twitter user ID from username
- **`get_last_tweet_and_replies_from_user`**: Get recent X/Twitter posts and replies from a user
- **`get_last_tweet_from_user`**: Get recent X/Twitter posts from a user
- **`get_own_twitter_account_info`**: Get current account profile data

## Installation and Usage

### Local Installation

```bash
# Clone the repository
git clone https://github.com/0xhijo/mcp_twitter.git

# Install dependencies and build the project
pnpm build

# Launch the server
node ./build/index.js
```
### Installation via NPX
```bash
npx mcp_twitter
```

## Configuration 

### Configuration via Twitter Scraper

1. Configure the .env file:

```sh
TWITTER_AUTH_MODE = "CREDENTIALS" # Credentials mode

# Your Twitter credentials

TWITTER_USERNAME="YOUR_TWITTER_USERNAME"
TWITTER_PASSWORD="YOUR_TWITTER_PASSWORD"
TWITTER_EMAIL="YOUR_TWITTER_EMAIL"
```

You need to configure Twitter authentication by creating a `.env` file or directly adding the variables to your environment.

### Configuration via Twitter API

1. Create a Developer Account:

Make sure you have a Twitter account
Visit the Developer Platform
Get your API credentials
Follow this guide if you need help creating your developer account

2. Configure the .env file

```sh
TWITTER_AUTH_MODE = "API" # API mode

# Your CREDENTIALS obtained from the Developer Platform

TWITTER_API="YOUR_TWITTER_API"
TWITTER_API_SECRET="YOUR_TWITTER_API_SECRET"
TWITTER_ACCESS_TOKEN="YOUR_TWITTER_ACCESS_TOKEN"
TWITTER_ACCESS_TOKEN_SECRET="YOUR_TWITTER_ACCESS_TOKEN_SECRET"
```
## Integrating with Claude
To use MCP Twitter with Claude, you need to add it to your `claude_mcp_config.json` file. This will allow Claude to interact with Twitter through the MCP server.

### Adding to Claude's MCP Configuration
Add the following entry to your `claude_mcp_config.json` file:

```json
"mcp_twitter": {
  "command": "npx",
  "args": ["mcp_twitter"],
  "env": {
    "TWITTER_AUTH_MODE": "CREDENTIALS",
    "TWITTER_USERNAME": "YOUR_TWITTER_USERNAME",
    "TWITTER_PASSWORD": "YOUR_TWITTER_PASSWORD",
    "TWITTER_EMAIL": "YOUR_TWITTER_EMAIL"
  }
}
```
Replace the placeholder credentials with your actual Twitter account information. This configuration will launch the MCP Twitter server using npx when Claude needs to interact with Twitter.

### Usage with Claude
Once configured, Claude will be able to use all the Twitter functionalities provided by the MCP server, such as creating posts, retrieving tweets, and more. You can simply ask Claude to perform Twitter actions, and it will utilize the MCP server to execute them.




## Important Notes
- Choose the authentication mode (API or CREDENTIALS) based on your needs
- Verify that your credentials are properly configured in the .env file
- Check the official documentation for more details about API limitations






```

--------------------------------------------------------------------------------
/src/types/index.ts:
--------------------------------------------------------------------------------

```typescript
export type TweetType = {
    id: string;
    content: string;
  };
  
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
    "compilerOptions": {
      "target": "ES2022",
      "module": "Node16",
      "moduleResolution": "Node16",
      "outDir": "./build",
      "rootDir": "./src",
      "strict": true,
      "esModuleInterop": true,
      "skipLibCheck": true,
      "forceConsistentCasingInFileNames": true
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules"]
  }
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "mcp_twitter",
  "version": "1.0.1",
  "main": "index.js",
  "type": "module",
  "bin": {
    "mcp_twitter": "./build/index.js"
  },
  "scripts": {
    "build": "tsc && chmod 755 build/index.js",
    "clean": "rm -rf build",
    "clean:all": "rm -rf build node_modules",
    "start": "node build/index.js"
  },
  "files": [
    "build"
  ],
  "keywords": [
    "twitter",
    "agent",
    "mcp"
  ],
  "author": "0xhijo",
  "license": "MIT",
  "description": "",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.7.0",
    "agent-twitter-client": "^0.0.18",
    "dotenv": "^16.4.7",
    "twitter-api-v2": "^1.19.1",
    "zod": "^3.24.2"
  },
  "devDependencies": {
    "@types/node": "^22.13.10",
    "typescript": "^5.8.2"
  }
}

```

--------------------------------------------------------------------------------
/src/schema/index.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';

export const createTwitterpostSchema = z.object({
  post: z.string().describe('This is the string you want to post on X'),
});

export const getLastUserXTweetSchema = z.object({
  account_name: z
    .string()
    .describe('This is the account_name you want to get the latest tweet'),
});

export const ReplyTweetSchema = z.object({
  tweet_id: z.string().describe('The tweet id you want to reply'),
  response_text: z
    .string()
    .describe('This is the response you will send to the tweet'),
});

export const getLastTweetsOptionsSchema = z.object({
  query: z
    .string()
    .describe(
      'The search query . Any Twitter-compatible query format can be used'
    ),
  maxTeets: z.number().describe('The max tweets you want to get'),
  reply: z
    .boolean()
    .describe('If you want to include replyed tweet in your request'),
});

export const FollowXUserFromUsernameSchema = z.object({
  username: z.string().describe('The username you want to follow'),
});

export const getTwitterProfileFromUsernameSchema = z.object({
  username: z.string().describe('The username you want to get the profile'),
});

export const getTwitterUserIdFromUsernameSchema = z.object({
  username: z.string().describe('The username you want get the user_id'),
});

export const getLastTweetsAndRepliesFromUserSchema = z.object({
  username: z
    .string()
    .describe('The username you want to get last tweets and replies'),
  maxTweets: z
    .number()
    .describe('The number of tweets/replies you want to get from a User')
    .optional(),
});

export const getLastTweetsFromUserSchema = z.object({
  username: z.string().describe('The username you want to get last tweets'),
  maxTweets: z
    .number()
    .describe('The number of tweets you want to get from a User')
    .optional(),
});

export const createAndPostTwitterThreadSchema = z.object({
  thread: z
    .array(z.string())
    .describe(
      'This is the array of where every index of this array contain a part of your thread'
    ),
});

export type getLastUserXTweetParams = z.infer<typeof getLastUserXTweetSchema>;
export type ReplyTweetParams = z.infer<typeof ReplyTweetSchema>;
export type getLastTweetsOptionsParams = z.infer<
  typeof getLastTweetsOptionsSchema
>;
export type FollowXUserFromUsernameParams = z.infer<
  typeof FollowXUserFromUsernameSchema
>;
export type getTwitterProfileFromUsernameParams = z.infer<
  typeof getTwitterProfileFromUsernameSchema
>;
export type getTwitterUserIdFromUsernameParams = z.infer<
  typeof getTwitterUserIdFromUsernameSchema
>;
export type getLastTweetsAndRepliesFromUserParams = z.infer<
  typeof getLastTweetsAndRepliesFromUserSchema
>;
export type getLastTweetsFromUserParams = z.infer<
  typeof getLastTweetsFromUserSchema
>;
export type createAndPostTwitterThreadParams = z.infer<
  typeof createAndPostTwitterThreadSchema
>;
export type creatTwitterPostParams = z.infer<typeof createTwitterpostSchema>;

```

--------------------------------------------------------------------------------
/src/interfaces/index.ts:
--------------------------------------------------------------------------------

```typescript
import { Scraper } from "agent-twitter-client";
import { TwitterApi } from "twitter-api-v2";

export interface TwitterScraperConfig {
  twitter_scraper: Scraper;
  twitter_id: string;
  twitter_username: string;
}

export interface TwitterApiConfig {
  twitter_api: string;
  twitter_api_secret: string;
  twitter_access_token: string;
  twitter_access_token_secret: string;
  twitter_api_client: TwitterApi;
}

export class TwitterManager {
  twitterManager: TwitterScraperConfig | TwitterApiConfig | undefined;
  twitter_auth_mode: "API" | "CREDENTIALS";
  constructor() {
    this.twitterManager = undefined;
    this.twitter_auth_mode = "API";
  }

  public async initializeTwitterManager(): Promise<void> {
    if (process.env.TWITTER_AUTH_MODE === "CREDENTIALS") {
      const username = process.env.TWITTER_USERNAME;
      const password = process.env.TWITTER_PASSWORD;
      const email = process.env.TWITTER_EMAIL;

      if (!username || !password) {
        throw new Error(
          "Error when try to initializeTwitterManager in CREDENTIALS twitter_auth_mode check your .env"
        );
      }
      const user_client = new Scraper();

      await user_client.login(username, password, email);
      const account = await user_client.me();
      if (!account) {
        throw new Error("Impossible to get your twitter account information");
      }
      const userClient: TwitterScraperConfig = {
        twitter_scraper: user_client,
        twitter_id: account?.userId as string,
        twitter_username: account?.username as string,
      };
      this.twitterManager = userClient;
      this.twitter_auth_mode = "CREDENTIALS";
    } else if (process.env.TWITTER_AUTH_MODE === "API") {
      const twitter_api = process.env.TWITTER_API;
      const twitter_api_secret = process.env.TWITTER_API_SECRET;
      const twitter_access_token = process.env.TWITTER_ACCESS_TOKEN;
      const twitter_access_token_secret =
        process.env.TWITTER_ACCESS_TOKEN_SECRET;

      if (
        !twitter_api ||
        !twitter_api_secret ||
        !twitter_access_token ||
        !twitter_access_token_secret
      ) {
        throw new Error(
          "Error when try to initializeTwitterManager in API twitter_auth_mode check your .env"
        );
      }

      const userClient = new TwitterApi({
        appKey: twitter_api,
        appSecret: twitter_api_secret,
        accessToken: twitter_access_token,
        accessSecret: twitter_access_token_secret,
      });
      if (!userClient) {
        throw new Error(
          "Error when trying to createn you Twitter API Account check your API Twitter CREDENTIALS"
        );
      }

      const apiConfig: TwitterApiConfig = {
        twitter_api: twitter_api,
        twitter_api_secret: twitter_api_secret,
        twitter_access_token: twitter_access_token,
        twitter_access_token_secret: twitter_access_token_secret,
        twitter_api_client: userClient,
      };
      this.twitterManager = apiConfig;
      this.twitter_auth_mode = "API";
    } else {
      throw new Error(
        "Error when try to initializeTwitterManager check your .env"
      );
    }
  }
  public getTwitterManager(): TwitterScraperConfig | TwitterApiConfig {
    if (!this.twitterManager) {
      throw new Error("Twitter Manager is undefined");
    }
    return this.twitterManager;
  }
  public getTwitterAuthMode(): "API" | "CREDENTIALS" {
    return this.twitter_auth_mode;
  }
}


export interface TwitterTool<P = any> {
  name: string;
  description: string;
  schema?: Zod.AnyZodObject;
  execute: (
    twitter: TwitterManager,
    params: P,
    plugins_manager?: any
  ) => Promise<unknown>;
}

```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { TwitterManager, TwitterTool } from "./interfaces/index.js";
import dotenv from "dotenv";
import {
  createAndPostTwitterThread,
  createTwitterpost,
  FollowXUserFromUsername,
  getLastTweetsAndRepliesFromUser,
  getLastTweetsFromUser,
  getLastTweetsOptions,
  getLastUserTweet,
  getOwnTwitterAccountInfo,
  getTwitterProfileFromUsername,
  getTwitterUserIdFromUsername,
  ReplyTweet,
} from "./tools/twitter.js";
import {
  createAndPostTwitterThreadSchema,
  createTwitterpostSchema,
  FollowXUserFromUsernameSchema,
  getLastTweetsAndRepliesFromUserSchema,
  getLastTweetsFromUserSchema,
  getLastTweetsOptionsSchema,
  getLastUserXTweetSchema,
  getTwitterProfileFromUsernameSchema,
  getTwitterUserIdFromUsernameSchema,
  ReplyTweetSchema,
} from "./schema/index.js";

dotenv.config();

// Create server instance
const server = new McpServer({
  name: "twitter",
  version: "1.0.0",
});

export const registerTools = (TwitterToolRegistry: TwitterTool[]) => {
  TwitterToolRegistry.push({
    name: "create_twitter_post",
    description: "Create new X/Twitter post",
    schema: createTwitterpostSchema,
    execute: createTwitterpost,
  });

  TwitterToolRegistry.push({
    name: "reply_twitter_tweet",
    description: "Reply to specific X/Twitter post by ID",
    schema: ReplyTweetSchema,
    execute: ReplyTweet,
  });

  TwitterToolRegistry.push({
    name: "get_last_tweet",
    description: "Get most recent post from specified X/Twitter account",
    schema: getLastUserXTweetSchema,
    execute: getLastUserTweet,
  });

  TwitterToolRegistry.push({
    name: "get_last_tweets_options",
    description: "Get specified number of posts matching search query",
    schema: getLastTweetsOptionsSchema,
    execute: getLastTweetsOptions,
  });

  TwitterToolRegistry.push({
    name: "create_and_post_twitter_thread",
    description: "Create and publish X/Twitter thread",
    schema: createAndPostTwitterThreadSchema,
    execute: createAndPostTwitterThread,
  });

  TwitterToolRegistry.push({
    name: "follow_twitter_from_username",
    description: "Follow X/Twitter user by username",
    schema: FollowXUserFromUsernameSchema,
    execute: FollowXUserFromUsername,
  });

  TwitterToolRegistry.push({
    name: "get_twitter_profile_from_username",
    description: "Get full X/Twitter profile data by username",
    schema: getTwitterProfileFromUsernameSchema,
    execute: getTwitterProfileFromUsername,
  });

  TwitterToolRegistry.push({
    name: "get_twitter_user_id_from_username",
    description: "Get X/Twitter user ID from username",
    schema: getTwitterUserIdFromUsernameSchema,
    execute: getTwitterUserIdFromUsername,
  });

  TwitterToolRegistry.push({
    name: "get_last_tweet_and_replies_from_user",
    description: "Get recent X/Twitter posts and replies from user",
    schema: getLastTweetsAndRepliesFromUserSchema,
    execute: getLastTweetsAndRepliesFromUser,
  });

  TwitterToolRegistry.push({
    name: "get_last_tweet_from_user",
    description: "Get recent X/Twitter posts from user",
    schema: getLastTweetsFromUserSchema,
    execute: getLastTweetsFromUser,
  });

  TwitterToolRegistry.push({
    name: "get_own_twitter_account_info",
    description: "Get current account profile data",
    execute: getOwnTwitterAccountInfo,
  });
};

export const RegisterToolInServer = async (twitter: TwitterManager) => {
  const tools: TwitterTool[] = [];
  await registerTools(tools);
  for (const tool of tools) {
    if (!tool.schema) {
      server.tool(tool.name, tool.description, async () => {
        const result = await tool.execute(twitter, {});
        return {
          content: [
            {
              type: "text",
              text: JSON.stringify(result),
            },
          ],
        };
      });
    } else {
      server.tool(
        tool.name,
        tool.description,
        tool.schema.shape,
        async (params: any, extra: any) => {
          const result = await tool.execute(twitter, params);
          return {
            content: [
              {
                type: "text",
                text: JSON.stringify(result),
              },
            ],
          };
        }
      );
    }
  }
};

const checkEnv = (auth_mode: string): boolean => {
  let key;
  if (auth_mode === "CREDENTIALS") {
    key = ["TWITTER_USERNAME", "TWITTER_PASSWORD", "TWITTER_EMAIL"];
  } else if (auth_mode === "API") {
    key = [
      "TWITTER_API",
      "TWITTER_API_SECRET",
      "TWITTER_ACCESS_TOKEN",
      "TWITTER_ACCESS_TOKEN_SECRET",
    ];
  } else {
    console.error(`Invalid auth_mode: ${auth_mode}`);
    return false;
  }
  for (const k of key) {
    const value = process.env[k];
    if (!value) {
      console.error(`Missing required environment variable: ${k}`);
      return false;
    }
    return true;
  }
  return true;
};

async function main() {
  const transport = new StdioServerTransport();
  if (!checkEnv(process.env.TWITTER_AUTH_MODE as string)) {
    console.error("Failed to initialize TwitterManager");
    process.exit(1);
  }
  const twitter = new TwitterManager();
  twitter.initializeTwitterManager();
  await RegisterToolInServer(twitter);
  await server.connect(transport);
  console.error("Weather MCP Server running on stdio");
}

main().catch((error) => {
  console.error("Fatal error in main():", error);
  process.exit(1);
});

```

--------------------------------------------------------------------------------
/src/tools/twitter.ts:
--------------------------------------------------------------------------------

```typescript
import { TwitterManager } from "../interfaces/index.js";
import {
    createAndPostTwitterThreadParams,
    creatTwitterPostParams,
  FollowXUserFromUsernameParams,
  getLastTweetsAndRepliesFromUserParams,
  getLastTweetsFromUserParams,
  getLastTweetsOptionsParams,
  getLastUserXTweetParams,
  getTwitterProfileFromUsernameParams,
  getTwitterUserIdFromUsernameParams,
  ReplyTweetParams,
} from "../schema/index.js";
import { TweetType } from "../types/index.js";

/**
 * Retrieves the latest tweet from a specified user
 * @param {TwitterManager} twitter - The Starknet twitter instance containing Twitter authentication
 * @param {getLastUserXTweetParams} params - Parameters containing the account name to fetch from
 * @returns {Promise<{status: string, post_id?: string, post_text?: string, error?: any}>} The latest tweet information or error
 * @throws {Error} When not in CREDENTIALS mode or client is undefined
 */
export const getLastUserTweet = async (
  twitter: TwitterManager,
  params: getLastUserXTweetParams
) => {
  try {
    console.log("GetLastUserTweet");
    if (twitter.getTwitterAuthMode() != "CREDENTIALS") {
      throw new Error("You need to be in CREDENTIALS twitter_auth_mode");
    }
    const twitter_manager = twitter.getTwitterManager();
    if (!twitter_manager) {
      throw new Error("twitter_client is undefined");
    }
    if (!("twitter_scraper" in twitter_manager)) {
      throw new Error(
        "Invalid twitter client configuration for this operation"
      );
    }
    const twitter_client = twitter_manager.twitter_scraper;
    if (!twitter_client) {
      throw new Error("twitter_client is undefined");
    }
    if (!twitter_client) {
      throw new Error("twitter_client is undefined");
    }

    const lastestTweet = await twitter_client.getLatestTweet(
      params.account_name
    );
    if (!lastestTweet) {
      throw new Error("Error trying to get the latest tweet");
    }
    return {
      status: "success",
      post_id: lastestTweet.id,
      post_text: lastestTweet.text,
    };
  } catch (error) {
    console.log(error);
    return {
      status: "failure",
      error: error,
    };
  }
};

/**
 * Searches tweets based on specific query and maximum tweet count
 * @param {TwitterManager} twitter - The Starknet twitter instance containing Twitter authentication
 * @param {getLastTweetsOptionsParams} params - Parameters containing search query and maximum tweets to fetch
 * @returns {Promise<{status: string, result?: TweetType[], error?: any}>} Collection of matching tweets or error
 * @throws {Error} When not in CREDENTIALS mode or client is undefined
 */
export const getLastTweetsOptions = async (
  twitter: TwitterManager,
  params: getLastTweetsOptionsParams
) => {
  try {
    console.log("GetLastTweetsOptions");
    if (twitter.getTwitterAuthMode() != "CREDENTIALS") {
      throw new Error("You need to be in CREDENTIALS twitter_auth_mode");
    }
    const twitter_manager = twitter.getTwitterManager();
    if (!twitter_manager) {
      throw new Error("twitter_client is undefined");
    }
    if (!("twitter_scraper" in twitter_manager)) {
      throw new Error(
        "Invalid twitter client configuration for this operation"
      );
    }
    const twitter_client = twitter_manager.twitter_scraper;
    if (!twitter_client) {
      throw new Error("twitter_client is undefined");
    }
    const collectedTweets: TweetType[] = [];

    const tweets = twitter_client.searchTweets(params.query, params.maxTeets);
    for await (const tweet of tweets) {
      const tweet_type: TweetType = {
        id: tweet.id as string,
        content: tweet.text as string,
      };
      console.log(tweet.id);
      console.log(tweet.text);
      collectedTweets.push(tweet_type);
    }
    console.log(collectedTweets);
    return {
      status: "success",
      result: collectedTweets,
    };
  } catch (error) {
    console.log(error);
    return {
      status: "failure",
      error: error,
    };
  }
};

/**
 * Retrieves information about the authenticated Twitter account set in .env
 * @param {TwitterManager} twitter - The Starknet twitter instance containing Twitter authentication
 * @returns {Promise<{status: string, my_account_username?: string, error?: any}>} Account information or error
 * @throws {Error} When not in CREDENTIALS mode or client is undefined
 */
export const getOwnTwitterAccountInfo = async (twitter: TwitterManager) => {
  try {
    console.log("getOwnTwitterAccountInfo");
    if (twitter.getTwitterAuthMode() != "CREDENTIALS") {
      throw new Error("You need to be in CREDENTIALS twitter_auth_mode");
    }
    const twitter_manager = twitter.getTwitterManager();
    if (!twitter_manager) {
      throw new Error("twitter_client is undefined");
    }
    if (!("twitter_scraper" in twitter_manager)) {
      throw new Error(
        "Invalid twitter client configuration for this operation"
      );
    }
    const twitter_client = twitter_manager.twitter_scraper;
    if (!twitter_client) {
      throw new Error("twitter_client is undefined");
    }
    if (!twitter_client) {
      throw new Error("twitter_client is undefined");
    }

    const my_twitter_account = await twitter_client.me();
    console.log(my_twitter_account);
    return {
      status: "success",
      my_account_username: my_twitter_account,
    };
  } catch (error) {
    console.log(error);
    return {
      status: "failure",
      error: error,
    };
  }
};

/**
 * Fetches recent tweets from a specified user
 * @param {TwitterManager} twitter - The Starknet twitter instance containing Twitter authentication
 * @param {getLastTweetsFromUserParams} params - Parameters containing username and optional tweet limit
 * @returns {Promise<{status: string, tweets?: TweetType[], error?: any}>} Collection of user's tweets or error
 * @throws {Error} When not in CREDENTIALS mode or client is undefined
 */
export const getLastTweetsFromUser = async (
  twitter: TwitterManager,
  params: getLastTweetsFromUserParams
) => {
  console.log("getLastTweetsFromUser");
  try {
    if (twitter.getTwitterAuthMode() != "CREDENTIALS") {
      throw new Error("You need to be in CREDENTIALS twitter_auth_mode");
    }
    const twitter_manager = twitter.getTwitterManager();
    if (!twitter_manager) {
      throw new Error("twitter_client is undefined");
    }
    if (!("twitter_scraper" in twitter_manager)) {
      throw new Error(
        "Invalid twitter client configuration for this operation"
      );
    }
    const twitter_client = twitter_manager.twitter_scraper;
    if (!twitter_client) {
      throw new Error("twitter_client is undefined");
    }
    if (!twitter_client) {
      throw new Error("twitter_client is undefined");
    }
    const tweets = params.maxTweets
      ? twitter_client.getTweets(params.username, params.maxTweets)
      : twitter_client.getTweets(params.username);
    const collectedTweets: TweetType[] = [];
    for await (const tweet of tweets) {
      const tweet_type: TweetType = {
        id: tweet.id as string,
        content: tweet.text as string,
      };
      collectedTweets.push(tweet_type);
    }

    return {
      status: "success",
      tweets: collectedTweets,
    };
  } catch (error) {
    console.log(error);
    return {
      status: "failure",
      error: error,
    };
  }
};

/**
 * Retrieves recent tweets and replies from a specified user
 * @param {TwitterManager} twitter - The Starknet twitter instance containing Twitter authentication
 * @param {getLastTweetsAndRepliesFromUserParams} params - Parameters containing username and optional tweet limit
 * @returns {Promise<{status: string, tweets?: TweetType[], error?: any}>} Collection of user's tweets and replies or error
 * @throws {Error} When not in CREDENTIALS mode or client is undefined
 */
export const getLastTweetsAndRepliesFromUser = async (
  twitter: TwitterManager,
  params: getLastTweetsAndRepliesFromUserParams
) => {
  try {
    console.log("getLastTweetsAndRepliesFromUser");
    if (twitter.getTwitterAuthMode() != "CREDENTIALS") {
      throw new Error("You need to be in CREDENTIALS twitter_auth_mode");
    }
    const twitter_manager = twitter.getTwitterManager();
    if (!twitter_manager) {
      throw new Error("twitter_client is undefined");
    }
    if (!("twitter_scraper" in twitter_manager)) {
      throw new Error(
        "Invalid twitter client configuration for this operation"
      );
    }
    const twitter_client = twitter_manager.twitter_scraper;
    if (!twitter_client) {
      throw new Error("twitter_client is undefined");
    }
    if (!twitter_client) {
      throw new Error("twitter_client is undefined");
    }
    const tweets = params.maxTweets
      ? twitter_client.getTweetsAndReplies(params.username, params.maxTweets)
      : twitter_client.getTweetsAndReplies(params.username);

    const collectedTweets: TweetType[] = [];
    for await (const tweet of tweets) {
      const tweet_type: TweetType = {
        id: tweet.id as string,
        content: tweet.text as string,
      };
      collectedTweets.push(tweet_type);
    }

    return {
      status: "success",
      tweets: collectedTweets,
    };
  } catch (error) {
    console.log(error);
    return {
      status: "failure",
      error: error,
    };
  }
};

/**
 * Gets Twitter user ID from a username
 * @param {TwitterManager} twitter - The Starknet twitter instance containing Twitter authentication
 * @param {getTwitterUserIdFromUsernameParams} params - Parameters containing the username to look up
 * @returns {Promise<{status: string, user_id?: string, error?: any}>} User ID information or error
 * @throws {Error} When not in CREDENTIALS mode or client is undefined
 */
export const getTwitterUserIdFromUsername = async (
  twitter: TwitterManager,
  params: getTwitterUserIdFromUsernameParams
) => {
  try {
    console.log("getTwitterUserIdFromUsername");
    if (twitter.getTwitterAuthMode() != "CREDENTIALS") {
      throw new Error("You need to be in CREDENTIALS twitter_auth_mode");
    }
    const twitter_manager = twitter.getTwitterManager();
    if (!twitter_manager) {
      throw new Error("twitter_client is undefined");
    }
    if (!("twitter_scraper" in twitter_manager)) {
      throw new Error(
        "Invalid twitter client configuration for this operation"
      );
    }
    const twitter_client = twitter_manager.twitter_scraper;
    if (!twitter_client) {
      throw new Error("twitter_client is undefined");
    }
    if (!twitter_client) {
      throw new Error("twitter_client is undefined");
    }
    const userId = await twitter_client.getUserIdByScreenName(params.username);
    return {
      status: "success",
      user_id: userId,
    };
  } catch (error) {
    console.log(error);
    return {
      status: "failure",
      error: error,
    };
  }
};

/**
 * Retrieves Twitter profile information from a username
 * @param {TwitterManager} twitter - The Starknet twitter instance containing Twitter authentication
 * @param {getTwitterProfileFromUsernameParams} params - Parameters containing the username to fetch profile for
 * @returns {Promise<{status: string, user_id?: any, error?: any}>} Profile information or error
 * @throws {Error} When not in CREDENTIALS mode, client is undefined, or account doesn't exist
 */
export const getTwitterProfileFromUsername = async (
  twitter: TwitterManager,
  params: getTwitterProfileFromUsernameParams
) => {
  try {
    console.log("geTwitterUserIdFromUsername");
    if (twitter.getTwitterAuthMode() != "CREDENTIALS") {
      throw new Error("You need to be in CREDENTIALS twitter_auth_mode");
    }
    const twitter_manager = twitter.getTwitterManager();
    if (!twitter_manager) {
      throw new Error("twitter_client is undefined");
    }
    if (!("twitter_scraper" in twitter_manager)) {
      throw new Error(
        "Invalid twitter client configuration for this operation"
      );
    }
    const twitter_client = twitter_manager.twitter_scraper;
    if (!twitter_client) {
      throw new Error("twitter_client is undefined");
    }
    if (!twitter_client) {
      throw new Error("twitter_client is undefined");
    }
    const userId = await twitter_client.getProfile(params.username);
    if (!userId) {
      throw new Error(`Account don't exist`);
    }
    return {
      status: "success",
      user_id: userId,
    };
  } catch (error) {
    console.log(error);
    return {
      status: "failure",
      error: error,
    };
  }
};

/**
 * Creates a new Twitter post using either credentials or API authentication
 * @param {TwitterManager} twitter - The Starknet twitter instance containing Twitter authentication
 * @param {creatTwitterPostParams} params - Parameters containing the post content
 * @returns {Promise<{status: string, result?: any}>} Result object indicating success/failure and optional API response
 * @throws {Error} When neither Twitter API nor Account credentials are set
 */
export const createTwitterpost = async (
  twitter: TwitterManager,
  params: creatTwitterPostParams
) => {
  try {
    const twitter_auth_mode = twitter.getTwitterAuthMode();
    if (twitter_auth_mode === "CREDENTIALS") {
      console.log("CREDENTIALS");
      const twitter_manager = twitter.getTwitterManager();
      if (!twitter_manager) {
        throw new Error("twitter_client is undefined");
      }
      if (!("twitter_scraper" in twitter_manager)) {
        throw new Error(
          "Invalid twitter client configuration for this operation"
        );
      }
      const twitter_client = twitter_manager.twitter_scraper;
      if (!twitter_client) {
        throw new Error("twitter_client is undefined");
      }
      await twitter_client.sendTweet(params.post);
      return {
        status: "success",
      };
    }
    if (twitter_auth_mode === "API") {
      const twitter_manager = twitter.getTwitterManager();
      if (!twitter_manager) {
        throw new Error("twitter_client is undefined");
      }
      if (!("twitter_api_client" in twitter_manager)) {
        throw new Error(
          "Invalid twitter client configuration for this operation"
        );
      }
      const twitter_client = twitter_manager.twitter_api_client;
      if (!twitter_client) {
        throw new Error("twitter_client is undefined");
      }

      const result = await twitter_client.v2.tweet({
        text: params.post,
      });
      return {
        status: "success",
        result: result,
      };
    } else {
      throw new Error(`You don't set Twitter API or Twitter Account`);
    }
  } catch (error) {
    console.log(error);
    return {
      status: "failed",
    };
  }
};

/**
 * Replies to a specific tweet using Twitter credentials
 * @param {TwitterManager} twitter - The Starknet twitter instance containing Twitter authentication
 * @param {ReplyTweetParams} params - Parameters containing the tweet ID to reply to and response text
 * @returns {Promise<{status: string, tweet_id?: string, response_text?: string, error?: any}>} Result object with operation status
 * @throws {Error} When not in CREDENTIALS mode or client is undefined
 */
export const ReplyTweet = async (
  twitter: TwitterManager,
  params: ReplyTweetParams
) => {
  try {
    if (twitter.getTwitterAuthMode() != "CREDENTIALS") {
      throw new Error("You need to be in CREDENTIALS twitter_auth_mode");
    }
    const twitter_manager = twitter.getTwitterManager();
    if (!twitter_manager) {
      throw new Error("twitter_client is undefined");
    }
    if (!("twitter_scraper" in twitter_manager)) {
      throw new Error(
        "Invalid twitter client configuration for this operation"
      );
    }
    const twitter_client = twitter_manager.twitter_scraper;
    if (!twitter_client) {
      throw new Error("twitter_client is undefined");
    }
    await twitter_client.sendTweet(params.response_text, params.tweet_id);
    return {
      status: "success",
      tweet_id: params.tweet_id,
      response_text: params.response_text,
    };
  } catch (error) {
    console.log(error);
    return {
      status: "failure",
      error: error,
    };
  }
};

/**
 * Creates and posts a Twitter thread from an array of messages
 * @param {TwitterManager} twitter - The Starknet twitter instance containing Twitter authentication
 * @param {createAndPostTwitterThreadParams} params - Parameters containing array of thread messages
 * @returns {Promise<{status: string, error?: any}>} Result object indicating thread posting status
 * @throws {Error} When thread is empty, not in CREDENTIALS mode, or client is undefined
 */
export const createAndPostTwitterThread = async (
  twitter: TwitterManager,
  params: createAndPostTwitterThreadParams
) => {
  try {
    console.log("CreateTwitterThread");
    const thread_size = params.thread.length;
    if (thread_size <= 0) {
      throw new Error("Your array of thread is empty");
    }
    if (twitter.getTwitterAuthMode() != "CREDENTIALS") {
      throw new Error("You need to be in CREDENTIALS twitter_auth_mode");
    }
    const twitter_manager = twitter.getTwitterManager();
    if (!twitter_manager) {
      throw new Error("twitter_client is undefined");
    }
    if (!("twitter_scraper" in twitter_manager)) {
      throw new Error(
        "Invalid twitter client configuration for this operation"
      );
    }
    const twitter_client = twitter_manager.twitter_scraper;
    if (!twitter_client) {
      throw new Error("twitter_client is undefined");
    }
    for (const [index, thread] of params.thread.entries()) {
      if (index === 0) {
        await twitter_client.sendTweet(thread);
        console.log(`Thread part ${index} = ${thread}`);
        continue;
      }
      let last_tweet_id;
      let conversation_id;
      const tweets = twitter_client.getTweetsAndRepliesByUserId(
        twitter_manager.twitter_id,
        10
      );
      let i = 0;
      for await (const tweet of tweets) {
        if (i === 0) {
          last_tweet_id = tweet.id;
          conversation_id = tweet.conversationId;
          i = 1;
          continue;
        }
        if (tweet.conversationId === conversation_id) {
          last_tweet_id = tweet.id;
          continue;
        }
      }

      await twitter_client.sendTweet(thread, last_tweet_id);
    }
    return {
      status: "success",
    };
  } catch (error) {
    console.log(error);
    return {
      status: "failure",
      error: error,
    };
  }
};

/**
 * Follows a Twitter user specified by username
 * @param {TwitterManager} twitter - The Starknet twitter instance containing Twitter authentication
 * @param {FollowXUserFromUsernameParams} params - Parameters containing the username to follow
 * @returns {Promise<{status: string, error?: any}>} Result object indicating follow operation status
 * @throws {Error} When not in CREDENTIALS mode or client is undefined
 */
export const FollowXUserFromUsername = async (
  twitter: TwitterManager,
  params: FollowXUserFromUsernameParams
) => {
  try {
    console.log("getXUserIdFromUsername");
    if (twitter.getTwitterAuthMode() != "CREDENTIALS") {
      throw new Error("You need to be in CREDENTIALS twitter_auth_mode");
    }
    const twitter_manager = twitter.getTwitterManager();
    if (!twitter_manager) {
      throw new Error("twitter_client is undefined");
    }
    if (!("twitter_scraper" in twitter_manager)) {
      throw new Error(
        "Invalid twitter client configuration for this operation"
      );
    }
    const twitter_client = twitter_manager.twitter_scraper;
    if (!twitter_client) {
      throw new Error("twitter_client is undefined");
    }
    await twitter_client.followUser(params.username);
    return {
      status: "success",
    };
  } catch (error) {
    console.log(error);
    return {
      status: "failure",
      error: error,
    };
  }
};

```