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
| ID | Twitter v2 |
|---|---|
| Provider | X Corp |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 6.5.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix 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 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-searchThis command will search for Tweets from the last 7 days and return all information available.
-
twitter-user-getLookup 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()