Twitter v2

Twitter integration provides access to searching recent Tweets (in last 7 days) and user information using the Twitter v2 API.

Data Enrichment & Threat Intelligence · Twitter

Details

IDTwitter v2
ProviderX Corp
CategoryData Enrichment & Threat Intelligence
From Version6.5.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Twitter integration provides access to searching recent Tweets (in last 7 days) and user information using the Twitter v2 API.
This integration was integrated and tested with version v2 of Twitter API.

Some changes have been made that might affect your existing content.
If you are upgrading from a previous of this integration, see Breaking Changes.

Configure Twitter v2 in Cortex

Parameter Description Required
Server URL   True
Bearer Token The Bearer Token to use for connection True
Trust any certificate (not secure)   False
Use system proxy settings   False

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

twitter-tweet-search


This command will search for Tweets from the last 7 days and return all information available.

Base Command

twitter-tweet-search

Input

Argument Name Description Required
query A comma-seperated list of keywords to submit to the recent search endpoint. Required
start_time The oldest UTC timestamp (from most recent seven days) from which the Tweets will be provided. Date format will be in ISO 8601 format (YYYY-MM-DDTHH:mm:ssZ) or relational expressions like “7 days ago”. Optional
end_time The most recent UTC timestamp to which the Tweets will be provided. Date format will be in ISO 8601 format (YYYY-MM-DDTHH:mm:ssZ) or relational expressions like “7 days ago”. Optional
limit Maximum number of results to return. Value can be between 10 and 100. Default is 50. Optional
next_token When you request a list of objects with a MaxResults setting, if the number of objects that are still available for retrieval exceeds the maximum you requested, Twitter returns a NextToken value in the response. To retrieve the next batch of objects, use the token returned from the prior request in your next request. Optional

Context Output

Path Type Description
Twitter.Tweet.conversation_id String The Tweet ID of the original Tweet of the conversation (which includes direct replies, replies of replies).
Twitter.Tweet.id String Unique identifier of this Tweet.
Twitter.Tweet.created_at Date Creation time of the Tweet.
Twitter.Tweet.text String The content of the Tweet.
Twitter.Tweet.edit_history_tweet_ids String Unique identifiers indicating all versions of an edited Tweet.
Twitter.Tweet.public_metrics.impression_count Number Number of times the Tweet has been seen.
Twitter.Tweet.public_metrics.retweet_count Number Number of times this Tweet has been Retweeted.
Twitter.Tweet.public_metrics.reply_count Number Number of replies to this Tweet.
Twitter.Tweet.public_metrics.like_count Number Number of Likes to this Tweet.
Twitter.Tweet.public_metrics.quote_count Number Number of times this Tweet has been Retweeted with a comment.
Twitter.Tweet.author.name String The unique identifier of this user.
Twitter.Tweet.author.verified Boolean Indicates if this user is a verified Twitter user.
Twitter.Tweet.author.description String The text of this user’s profile description (also known as bio), if the user provided one.
Twitter.Tweet.author.id String The unique identifier of this user.
Twitter.Tweet.author.created_at Date The UTC datetime when the user account was created on Twitter.
Twitter.Tweet.author.username String The Twitter screen name, handle, or alias that this user identifies themselves with.
Twitter.Tweet.media.type String Type of content (animated_gif, photo, video).
Twitter.Tweet.media.url String A direct URL to the media file on Twitter.
Twitter.Tweet.media.media_key String Unique identifier of the expanded media content
Twitter.Tweet.media.alt_text String A description of an image to enable and support accessibility. Can be up to 1000 characters long.
Twitter.TweetNextToken String A value that encodes the next ‘page’ of results that can be requested, via the next_token request parameter.

Command example

!twitter-tweet-search query="twitter" limit="10"

Context Example

{
    "Twitter": {
        "Tweet":  [
                {
                    "author": {
                        "created_at": "2023-01-18T23:35:28.000Z",
                        "description": "some_description",
                        "id": "2929292929292929292",
                        "name": "some_name_1",
                        "username": "some_username_1",
                        "verified": false
                    },
                    "conversation_id": "2323232323232323232",
                    "created_at": "2023-04-05T08:49:23.000Z",
                    "edit_history_tweet_ids": [
                        "2323232323232323232"
                    ],
                    "id": "2323232323232323232",
                    "public_metrics": {
                        "impression_count": 0,
                        "like_count": 0,
                        "quote_count": 0,
                        "reply_count": 0,
                        "retweet_count": 5822
                    },
                    "text": "some_text_twitter"
                },
                {
                    "author": {
                        "created_at": "2017-10-19T18:40:34.000Z",
                        "description": "some_description",
                        "id": "2020202020202020202",
                        "name": "some_name_2",
                        "username": "some_username_2",
                        "verified": false
                    },
                    "conversation_id": "1010101010101010101",
                    "created_at": "2023-04-05T08:49:23.000Z",
                    "edit_history_tweet_ids": [
                        "1010101010101010101"
                    ],
                    "id": "1010101010101010101",
                    "public_metrics": {
                        "impression_count": 0,
                        "like_count": 0,
                        "quote_count": 0,
                        "reply_count": 0,
                        "retweet_count": 20
                    },
                    "text": "some_text_twitter"
                },
                {
                    "author": {
                        "created_at": "2023-02-02T07:40:50.000Z",
                        "description": "some_description",
                        "id": "1313131313131313131",
                        "name": "some_name_3",
                        "username": "some_username_3",
                        "verified": false
                    },
                    "conversation_id": "1515151515151515151",
                    "created_at": "2023-04-05T08:49:23.000Z",
                    "edit_history_tweet_ids": [
                        "1515151515151515151"
                    ],
                    "id": "1515151515151515151",
                    "media": [
                        {
                            "media_key": "4_4444444444444444444",
                            "type": "photo",
                            "url": "https://url.jpg"
                        }
                    ],
                    "public_metrics": {
                        "impression_count": 1,
                        "like_count": 0,
                        "quote_count": 0,
                        "reply_count": 0,
                        "retweet_count": 0
                    },
                    "text": "some_text_twitter"
                },
                {
                    "author": {
                        "created_at": "2017-03-25T01:59:34.000Z",
                        "description": "some_description",
                        "id": "845455085635293184",
                        "name": "some_name_5",
                        "username": "some_username_5",
                        "verified": false
                    },
                    "conversation_id": "1212121212121212121",
                    "created_at": "2023-04-05T08:49:23.000Z",
                    "edit_history_tweet_ids": [
                        "1212121212121212121"
                    ],
                    "id": "1212121212121212121",
                    "public_metrics": {
                        "impression_count": 0,
                        "like_count": 0,
                        "quote_count": 0,
                        "reply_count": 0,
                        "retweet_count": 114
                    },
                    "text": "some_text_twitter"
                },
                {
                    "author": {
                        "created_at": "2014-04-21T09:26:32.000Z",
                        "description": "some_description",
                        "id": "2456260950",
                        "name": "some_name_4",
                        "username": "some_username_4",
                        "verified": false
                    },
                    "conversation_id": "0808080808080808080",
                    "created_at": "2023-04-05T08:49:23.000Z",
                    "edit_history_tweet_ids": [
                        "0808080808080808080"
                    ],
                    "id": "0808080808080808080",
                    "media": [
                        {
                            "media_key": "5_5555555555555555555",
                            "type": "photo",
                            "url": "https://url.jpg"
                        }
                    ],
                    "public_metrics": {
                        "impression_count": 0,
                        "like_count": 0,
                        "quote_count": 0,
                        "reply_count": 0,
                        "retweet_count": 846
                    },
                    "text": "some_text_twitter"
                },
                {
                    "author": {
                        "created_at": "2017-07-18T14:56:15.000Z",
                        "description": "some_description",
                        "id": "2424242424242424242",
                        "name": "some_name_6",
                        "username": "some_username_6",
                        "verified": false
                    },
                    "conversation_id": "0707070707070707070",
                    "created_at": "2023-04-05T08:49:23.000Z",
                    "edit_history_tweet_ids": [
                        "0707070707070707070"
                    ],
                    "id": "0707070707070707070",
                    "media": [
                        {
                            "media_key": "3_3333333333333333333",
                            "type": "photo",
                            "url": "https://url.jpg"
                        }
                    ],
                    "public_metrics": {
                        "impression_count": 0,
                        "like_count": 0,
                        "quote_count": 0,
                        "reply_count": 0,
                        "retweet_count": 0
                    },
                    "text": "some_text_twitter"
                },
                {
                    "author": {
                        "created_at": "2022-05-08T07:49:51.000Z",
                        "description": "some_description",
                        "id": "6060606060606006060",
                        "name": "some_name_7",
                        "username": "some_username_7",
                        "verified": false
                    },
                    "conversation_id": "5050505050505050505",
                    "created_at": "2023-04-05T08:49:23.000Z",
                    "edit_history_tweet_ids": [
                        "5050505050505050505"
                    ],
                    "id": "5050505050505050505",
                    "public_metrics": {
                        "impression_count": 0,
                        "like_count": 0,
                        "quote_count": 0,
                        "reply_count": 0,
                        "retweet_count": 73
                    },
                    "text": "some_text_twitter"
                },
                {
                    "author": {
                        "created_at": "2022-10-11T11:11:10.000Z",
                        "description": "some_description",
                        "id": "4040404040404040404",
                        "name": "some_name_8",
                        "username": "some_username_8",
                        "verified": false
                    },
                    "conversation_id": "3030303030303030303",
                    "created_at": "2023-04-05T08:49:23.000Z",
                    "edit_history_tweet_ids": [
                        "3030303030303030303"
                    ],
                    "id": "3030303030303030303",
                    "public_metrics": {
                        "impression_count": 0,
                        "like_count": 0,
                        "quote_count": 0,
                        "reply_count": 0,
                        "retweet_count": 37
                    },
                    "text": "some_text_twitter"
                },
                {
                    "author": {
                        "created_at": "2020-07-09T13:06:54.000Z",
                        "description": "",
                        "id": "2727272727272727272",
                        "name": "some_name_9",
                        "username": "some_username_9",
                        "verified": false
                    },
                    "conversation_id": "2626262626262626262",
                    "created_at": "2023-04-05T08:49:23.000Z",
                    "edit_history_tweet_ids": [
                        "2626262626262626262"
                    ],
                    "id": "2626262626262626262",
                    "public_metrics": {
                        "impression_count": 0,
                        "like_count": 0,
                        "quote_count": 0,
                        "reply_count": 0,
                        "retweet_count": 4273
                    },
                    "text": "some_text_twitter"
                },
                {
                    "author": {
                        "created_at": "2017-01-15T11:11:11.000Z",
                        "description": "some_description",
                        "id": "2828282828282828282",
                        "name": "some_name_10",
                        "username": "some_username_10",
                        "verified": false
                    },
                    "conversation_id": "2525252525252525252",
                    "created_at": "2023-04-05T08:49:23.000Z",
                    "edit_history_tweet_ids": [
                        "2525252525252525252"
                    ],
                    "id": "2525252525252525252",
                    "public_metrics": {
                        "impression_count": 0,
                        "like_count": 0,
                        "quote_count": 0,
                        "reply_count": 0,
                        "retweet_count": 22741
                    },
                    "text": "some_text_twitter"
                }
            ],
        "TweetNextToken": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    }
}

Human Readable Output

Tweet Next Token

next_token
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Tweets search results

Tweet ID Text Created At Author Name Author Username Likes Count Attachments URL
2323232323232323232 some_text_twitter 2023-04-05T08:49:23.000Z some_name_1 some_username_1 0  
1010101010101010101 some_text_twitter 2023-04-05T08:49:23.000Z some_name_2 some_username_2 0  
1515151515151515151 some_text_twitter 2023-04-05T08:49:23.000Z some_name_3 some_username_3 0 https://url.jpg
1212121212121212121 some_text_twitter 2023-04-05T08:49:23.000Z some_name_5 some_username_5 0  
0808080808080808080 some_text_twitter 2023-04-05T08:49:23.000Z some_name_4 some_username_4 0 https://url.jpg
0707070707070707070 some_text_twitter 2023-04-05T08:49:23.000Z some_name_6 some_username_6 0 https://url.jpg
5050505050505050505 some_text_twitter 2023-04-05T08:49:23.000Z some_name_7 some_username_7 0  
3030303030303030303 some_text_twitter 2023-04-05T08:49:23.000Z some_name_8 some_username_8 0  
2626262626262626262 some_text_twitter 2023-04-05T08:49:23.000Z some_name_9 some_username_9 0  
2525252525252525252 some_text_twitter 2023-04-05T08:49:23.000Z some_name_10 some_username_10 0  

twitter-user-get


Lookup users by name to display information about them. Search multiple users simultaneously by separating them by commas. Ex: ‘name=’user1,user2,user3’

Base Command

twitter-user-get

Input

Argument Name Description Required
user_name A comma-separated list of Twitter usernames (handles). Up to 100 are allowed in a single request. Required
return_pinned_tweets Indicates whether to return a user’s pinned Tweets. Possible values are: true, false. Default is false. Optional

Context Output

Path Type Description
Twitter.User.name String The friendly name of this user, as shown on their profile.
Twitter.User.username String The Twitter handle (screen name) of this user.
Twitter.User.created_at Date Creation time of this account.
Twitter.User.description String The text of this user’s profile description (also known as bio), if the user provided one.
Twitter.User.id String Unique identifier of this user.
Twitter.User.location String The location specified in the user’s profile.
Twitter.User.pinned_tweet_id String Unique identifier of this user’s pinned Tweet.
Twitter.User.profile_image_url String The URL to the profile image for this user, as shown on the user’s profile.
Twitter.User.protected Boolean Indicates if this user has chosen to protect their Tweets (in other words, if this user’s Tweets are private).
Twitter.User.public_metrics.followers_count Number Number of users who follow this user.
Twitter.User.public_metrics.following_count Number Number of users this user is following.
Twitter.User.public_metrics.tweet_count Number Number of Tweets (including Retweets) posted by this user.
Twitter.User.public_metrics.listed_count Number Number of lists that include this user.
Twitter.User.url String The URL specified in the user’s profile, if present.
Twitter.User.verified Boolean Indicates if this user is a verified Twitter user.
Twitter.User.withheld String Contains withholding details for withheld content.
Twitter.User.entities.url String Contains details about the user’s profile website.
Twitter.User.entities.expanded_url String The fully resolved URL.
Twitter.User.entities.display_url String The URL as displayed in the user’s profile.
Twitter.User.pinned_tweets.id String Unique identifier of this user’s pinned Tweet.
Twitter.User.pinned_tweets.text String The content of the Tweet.
Twitter.User.pinned_tweets.conversation_id String The Tweet ID of the original Tweet of the conversation (which includes direct replies, replies of replies).
Twitter.User.pinned_tweets.created_at Date Creation time of the Tweet.
Twitter.User.pinned_tweets.edit_history_tweet_ids String Unique identifiers indicating all versions of an edited Tweet.
Twitter.User.pinned_tweets.retweet_count Number Number of times this Tweet has been Retweeted.
Twitter.User.pinned_tweets.reply_count Number Number of Replies to this Tweet.
Twitter.User.Pinned_tweets.like_count Number Number of Likes to this Tweet.
Twitter.User.pinned_tweets.quote_count Number Number of times this Tweet has been Retweeted with a comment.

Command example

!twitter-user-get user_name="Twitter"

Context Example

{
    "Twitter": {
        "User": {
            "created_at": "2006-06-15T14:35:54.000Z",
            "description": "description",
            "entities": [
                {
                    "display_url": "url.com",
                    "expanded_url": "https://url.com/",
                    "url": "https://url"
                }
            ],
            "id": "111111",
            "location": "everywhere",
            "name": "Twitter",
            "profile_image_url": "https://url.jpg",
            "protected": false,
            "public_metrics": {
                "followers_count": 65450397,
                "following_count": 5,
                "listed_count": 87323,
                "tweet_count": 15046
            },
            "url": "https://url",
            "username": "Twitter",
            "verified": true
        }
    }
}

Human Readable Output

twitter user get results

Name User name Created At Description Followers Count Tweet Count verified
Twitter Twitter 2006-06-15T14:35:54.000Z description 11111111 15046 true

Breaking changes from the previous version of this integration - Twitter v2

The following sections lists the changes in this version.

Commands

The following commands were removed in this version

  • twitter-get-user-info - this command was removed.
  • twitter-get-users - this command was replaced by twitter-user-get.
  • twitter-get-tweets - this command was replaced by twitter-tweet-search.

Additional Considerations for this version

Only a Bearer Token is needed in order to configure this integration.

Configuration parameters

  • url — Server URL (required)
  • credentials — (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (2)

  • twitter-tweet-search

    This command will search for Tweets from the last 7 days and return all information available.

  • twitter-user-get

    Lookup users by name to display information about them. Search multiple users simultaneously by separating them by commas. Ex: 'name='user1,user2,user3'.

import demistomock as demisto
from CommonServerPython import *  # noqa # pylint: disable=unused-wildcard-import
from CommonServerUserPython import *  # noqa
import urllib3
from typing import Any

# Disable insecure warnings
urllib3.disable_warnings()


""" CONSTANTS """

DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"  # ISO8601 format with UTC, default in XSOAR

""" CLIENT CLASS """


class Client(BaseClient):
    def tweet_search(
        self, query: str, start_time: Optional[str], end_time: Optional[str], limit: Optional[int], next_token: Optional[str]
    ) -> dict:
        """Gets tweets according to the query.
        Args:
            query: str - The query from the user.
            start_time: str - Start date from which the Tweets will be provided.
            end_time: str - The most recent date to which the Tweets will be provided.
            limit: int - Maximum number of results to return.
            next_token: str - A value that encodes the next 'page' of results that can be requested.

        Returns:
            List[dict]: raw response.
        """
        query_params = {
            "query": "".join(f'"{item}"' for item in query),
            "tweet.fields": "id,text,attachments,author_id,conversation_id,created_at,public_metrics",
            "expansions": "attachments.media_keys,author_id",
            "media.fields": "media_key,type,url,public_metrics,alt_text",
            "user.fields": "id,name,username,created_at,description,verified",
            "max_results": f"{limit}",
            "start_time": start_time,
            "end_time": end_time,
            "next_token": next_token,
        }
        response = self._http_request(
            method="GET", url_suffix="/tweets/search/recent", headers=self._headers, params=query_params, ok_codes=[200]
        )
        return response

    def twitter_user_get(self, users_names: list[str], return_pinned_tweets: str) -> dict:
        """Gets users according to the provided user names.
        Args:
            user_name: list[str] - List of users names.
            return_pinned_tweets: str - Indicates whether to return user's pinned Tweets.
            limit: int - Maximum number of results to return.

        Returns:
            dict: raw response.
        """
        response = {}
        query_params = {
            "usernames": ",".join(f"{item}" for item in users_names),
            "user.fields": "created_at,description,entities,id,location,name,pinned_tweet_id,profile_image_url"
            ",protected,public_metrics,url,username,verified,withheld",
        }
        if return_pinned_tweets == "true":
            query_params["expansions"] = "pinned_tweet_id"
            query_params["tweet.fields"] = "id,text,attachments,conversation_id,created_at,public_metrics"
        response = self._http_request(
            method="GET", url_suffix="/users/by", headers=self._headers, params=query_params, ok_codes=[200]
        )
        return response


""" HELPER FUNCTIONS """


def create_context_data_search_tweets(response: dict) -> tuple[List[dict], str]:
    """Gets raw response form Twitter API and extracts the relevent data.
    The data matched by
    attachments.media_keys == includes.media.media_key
    and author_id == includes.users.id.
        Args:
            response: dict - raw response form Twitter API.
        Returns:
            A tuple[dict, str] with:
            dict: context data.
            str: next token.
    """
    include = response.get("includes", {})
    data = response.get("data", [])
    users = include.get("users", [])
    media = include.get("media", [])
    next_token = response.get("meta", {}).get("next_token")
    list_dict_response = []
    for data_item in data:
        author_id = data_item.get("author_id")
        for user in users:
            id = user.get("id")
            if author_id == id:
                dict_to_append = {
                    "id": data_item.get("id"),
                    "text": data_item.get("text"),
                    "conversation_id": data_item.get("conversation_id"),
                    "created_at": data_item.get("created_at"),
                    "edit_history_tweet_ids": data_item.get("edit_history_tweet_ids"),
                    "author": {
                        "id": user.get("id"),
                        "description": user.get("description"),
                        "name": user.get("name"),
                        "verified": user.get("verified"),
                        "username": user.get("username"),
                        "created_at": user.get("created_at"),
                    },
                    "public_metrics": data_item.get("public_metrics", {}),
                    "media": [],
                }
                attachments = data_item.get("attachments", {})
                if attachments:
                    media_to_append = []
                    media_keys = attachments.get("media_keys", [])
                    for media_key in media_keys:
                        for media_item in media:
                            media_key_attachments = media_item.get("media_key")
                            if media_key == media_key_attachments:
                                media_to_append.append(
                                    {
                                        "url": media_item.get("url"),
                                        "media_key": media_item.get("media_key"),
                                        "alt_text": media_item.get("alt_text"),
                                        "type": media_item.get("type"),
                                    }
                                )
                    dict_to_append["media"] = media_to_append
                list_dict_response.append(remove_empty_elements(dict_to_append))
    return list_dict_response, next_token


def create_context_data_get_user(response: dict, pinned_tweets: str) -> list[dict]:
    """Gets raw response form Twitter API and extracts the relevent data.
    The data matched by pinned_tweet_id == includes.tweets.id
        Args:
            response: dict - raw response form Twitter API.
        Returns:
            dict: context data.
    """
    include = response.get("includes", {})
    data = response.get("data", {})
    tweets = include.get("tweets")
    list_dict_response = []
    for data_item in data:
        pinned_tweet_id = data_item.get("pinned_tweet_id")
        dict_to_append = {
            "name": data_item.get("name"),
            "username": data_item.get("username"),
            "created_at": data_item.get("created_at"),
            "description": data_item.get("description"),
            "id": data_item.get("id"),
            "location": data_item.get("location"),
            "pinned_tweet_id": data_item.get("pinned_tweet_id"),
            "profile_image_url": data_item.get("profile_image_url"),
            "protected": data_item.get("protected"),
            "url": data_item.get("url"),
            "verified": data_item.get("verified"),
            "withheld": data_item.get("withheld"),
            "public_metrics": data_item.get("public_metrics", {}),
            "entities": [
                {"url": item.get("url"), "expanded_url": item.get("expanded_url"), "display_url": item.get("display_url")}
                for item in data_item.get("entities", {}).get("url", {}).get("urls", {})
            ],
        }
        if tweets and pinned_tweet_id and pinned_tweets:
            for tweet in tweets:
                tweet_id = tweet.get("id")
                if pinned_tweet_id == tweet_id:
                    dict_to_append["pinned_Tweets"] = {
                        "id": tweet.get("id"),
                        "text": tweet.get("text"),
                        "conversation_id": tweet.get("conversation_id"),
                        "edit_history_tweet_ids": tweet.get("edit_history_tweet_ids"),
                    }
        list_dict_response.append(remove_empty_elements(dict_to_append))
    return list_dict_response


""" COMMAND FUNCTIONS """


def test_module(client: Client) -> str:
    """Tests API connectivity and authentication'

    Returning 'ok' indicates that the integration works like it is supposed to.
    Connection to the service is successful.
    Raises exceptions if something goes wrong.

    :type client: ``Client``
    :param Client: client to use

    :return: 'ok' if test passed, anything else will fail the test.
    :rtype: ``str``
    """

    client.tweet_search("Twitter", None, None, 10, None)
    message = "ok"
    return message


def date_to_iso_format(date: Optional[str]) -> Optional[str]:
    """Retrieves date string or relational expression to iso format date.
    Args:
        date: str - date or relational expression.
    Returns:
        A str in ISO format or None.
    """
    if date:
        datetime = dateparser.parse(date)
        if datetime:
            date = datetime.strftime("%Y-%m-%dT%H:%M:%SZ")
        else:
            raise DemistoException("Twitter: Date format is invalid")
    return date


def create_human_readable_search(dict_list: list[dict]) -> list[dict]:
    """Gets list of dictionaries and creates a human readable from it.
    Args:
        dict_list: list[dict] -  A list of dictionaries.
    Returns:
        human readable: list[dict].
    """
    list_dict_response: list = []
    for dict_value in dict_list:
        list_dict_response.append(
            {
                "Tweet ID": dict_value.get("id", {}),
                "Text": dict_value.get("text", ""),
                "Created At": dict_value.get("created_at", {}),
                "Author Name": dict_value.get("author", {}).get("name"),
                "Author Username": dict_value.get("author", {}).get("username"),
                "Likes Count": dict_value.get("public_metrics", {}).get("like_count"),
                "Attachments URL": [item.get("url", "") for item in dict_value.get("media", [])]
                if dict_value.get("media", [])
                else [],
            }
        )
    return list_dict_response


def twitter_tweet_search_command(client: Client, args: dict[str, Any]) -> List[CommandResults]:
    """Gets args and client and returns CommandResults of Tweets according to the reqested search.
    Args:
        client: client -  A Twitter client.
        args: Dict - The function arguments.
    Returns:
        A list of CommandResults with Tweets data according to to the reqest.
    """
    headers = ["Tweet ID", "Text", "Created At", "Author Name", "Author Username", "Likes Count", "Attachments URL"]
    query = argToList(args.get("query"))
    start_time = date_to_iso_format(args.get("start_time"))
    end_time = date_to_iso_format(args.get("end_time"))
    limit = arg_to_number(args.get("limit", 50))
    next_token = args.get("next_token")
    raw_response = client.tweet_search(query, start_time, end_time, limit, next_token)
    context_data, next_token = create_context_data_search_tweets(raw_response)
    dict_to_tableToMarkdown = create_human_readable_search(context_data)
    human_readable = tableToMarkdown("Tweets search results:", dict_to_tableToMarkdown, headers=headers, removeNull=False)
    command_results = []
    command_results.append(
        CommandResults(
            outputs_prefix="Twitter.Tweet",
            outputs_key_field="id",
            outputs=context_data,
            readable_output=human_readable,
            raw_response=raw_response,
        )
    )
    if next_token:
        readable_output_next_token = tableToMarkdown(
            "Tweet Next Token:", {"next_token": next_token}, headers=["next_token"], removeNull=False
        )
        command_results.append(
            CommandResults(
                outputs={"Twitter(true)": {"TweetNextToken": next_token}},
                readable_output=readable_output_next_token,
            )
        )

    return command_results


def twitter_user_get_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """Retruns users information according to the requested user's names.
    Args:
        client: client -  A Twitter client.
        args: Dict - The function arguments.
    Returns:
        A CommandResult object with users' data according to the request.
    """
    headers = ["Name", "User name", "Created At", "Description", "Followers Count", "Tweet Count", "Verified"]
    user_name = argToList(args.get("user_name"))
    return_pinned_tweets = args.get("return_pinned_tweets", "false")
    raw_response = client.twitter_user_get(user_name, return_pinned_tweets)
    context_data = create_context_data_get_user(raw_response, return_pinned_tweets)
    contents: list = []
    for dict_value in context_data:
        contents.append(
            {
                "Name": dict_value.get("name"),
                "User name": dict_value.get("username"),
                "Created At": dict_value.get("created_at"),
                "Description": dict_value.get("description"),
                "Followers Count": dict_value.get("public_metrics", {}).get("followers_count"),
                "Tweet Count": dict_value.get("public_metrics", {}).get("tweet_count"),
                "Verified": dict_value.get("verified"),
            }
        )
    human_readable = tableToMarkdown("Twitter user get results:", contents, headers=headers, removeNull=False)
    return CommandResults(
        outputs_prefix="Twitter.User",
        outputs_key_field="id",
        outputs=context_data,
        readable_output=human_readable,
        raw_response=raw_response,
    )


""" MAIN FUNCTION """


def main() -> None:
    bearer_token = demisto.params().get("credentials", {}).get("password")

    # get the service API url
    base_url = urljoin(demisto.params()["url"], "/2")
    verify_certificate = not demisto.params().get("insecure", False)
    proxy = demisto.params().get("proxy", False)

    demisto.debug(f"Command being called is {demisto.command()}")
    try:
        headers: dict = {"Authorization": f"Bearer {bearer_token}"}
        client = Client(base_url=base_url, verify=verify_certificate, headers=headers, proxy=proxy)

        if demisto.command() == "test-module":
            # This is the call made when pressing the integration Test button.
            result = test_module(client)
            return_results(result)

        elif demisto.command() == "twitter-tweet-search":
            return_results(twitter_tweet_search_command(client, demisto.args()))

        elif demisto.command() == "twitter-user-get":
            return_results(twitter_user_get_command(client, demisto.args()))

    # Log exceptions and return errors
    except Exception as e:
        return_error(f"Failed to execute {demisto.command()} command.\nError:\n{str(e)}")


""" ENTRY POINT """


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()