1
Fork 0
mirror of https://github.com/thegeneralist01/twitter-openapi synced 2026-03-07 12:39:54 +01:00

Merge pull request #90 from fa0311/dev

Dev
This commit is contained in:
ふぁ 2025-05-07 22:10:05 +09:00 committed by GitHub
commit 54acb4a0ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 4063 additions and 995 deletions

View file

@ -40,7 +40,7 @@ jobs:
- name: Get Openapi Generator - name: Get Openapi Generator
run: | run: |
wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.12.0/openapi-generator-cli-7.12.0.jar -O openapi-generator-cli.jar --no-verbose wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.13.0/openapi-generator-cli-7.13.0.jar -O openapi-generator-cli.jar --no-verbose
if: steps.openapi-generator-cache.outputs.cache-hit != 'true' if: steps.openapi-generator-cache.outputs.cache-hit != 'true'
# Python Setup # Python Setup

7
.vscode/tasks.json vendored
View file

@ -24,6 +24,7 @@
"command": [ "command": [
".venv/Scripts/activate;", ".venv/Scripts/activate;",
"python tools/build.py;", "python tools/build.py;",
"scoop reset temurin17-jdk;",
"java -jar openapi-generator-cli.jar generate -c test/python/openapi-generator-config.yaml -g python;", "java -jar openapi-generator-cli.jar generate -c test/python/openapi-generator-config.yaml -g python;",
"python -m pip install ./python_generated;" "python -m pip install ./python_generated;"
] ]
@ -40,7 +41,7 @@
"python3.10 -m venv .venv;", "python3.10 -m venv .venv;",
".venv/bin/python3 -m pip install -r requirements.txt;", ".venv/bin/python3 -m pip install -r requirements.txt;",
"pip install urllib3>=2.1.0;", "pip install urllib3>=2.1.0;",
"curl https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.12.0/openapi-generator-cli-7.12.0.jar -o openapi-generator-cli.jar;" "curl https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.13.0/openapi-generator-cli-7.13.0.jar -o openapi-generator-cli.jar;"
] ]
}, },
"linux": { "linux": {
@ -48,7 +49,7 @@
"python3 -m venv .venv;", "python3 -m venv .venv;",
".venv/bin/python3 -m pip install -r requirements.txt;", ".venv/bin/python3 -m pip install -r requirements.txt;",
"pip install urllib3>=2.1.0;", "pip install urllib3>=2.1.0;",
"wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.12.0/openapi-generator-cli-7.12.0.jar -O openapi-generator-cli.jar;" "wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.13.0/openapi-generator-cli-7.13.0.jar -O openapi-generator-cli.jar;"
] ]
}, },
"windows": { "windows": {
@ -56,7 +57,7 @@
"python -m venv .venv;", "python -m venv .venv;",
".venv/Scripts/python -m pip install -r requirements.txt;", ".venv/Scripts/python -m pip install -r requirements.txt;",
"pip install urllib3>=2.1.0;", "pip install urllib3>=2.1.0;",
"Invoke-WebRequest https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.12.0/openapi-generator-cli-7.12.0.jar -OutFile openapi-generator-cli.jar;" "Invoke-WebRequest https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.13.0/openapi-generator-cli-7.13.0.jar -OutFile openapi-generator-cli.jar;"
] ]
} }
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -60,6 +60,62 @@ paths:
tags: tags:
- "tweet" - "tweet"
/graphql/{pathQueryId}/CommunityTweetsTimeline:
get:
operationId: getCommunityTweetsTimeline
description: get tweet list of community. rankingMode:[Recency, Relevance]
responses:
"200":
description: Successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/CommunityTweetsTimelineResponse"
tags:
- "tweet"
/graphql/{pathQueryId}/CommunityMediaTimeline:
get:
operationId: getCommunityMediaTimeline
description: get media list of community
responses:
"200":
description: Successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/CommunityMediaTimelineResponse"
tags:
- "tweet"
/graphql/{pathQueryId}/CommunityAboutTimeline:
get:
operationId: getCommunityAboutTimeline
description: get about of community
responses:
"200":
description: Successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/CommunityAboutTimelineResponse"
tags:
- "tweet"
/graphql/{pathQueryId}/NotificationsTimeline:
get:
operationId: getNotificationsTimeline
description: get notification list. timeline_type:[All, Verified, Mentions]
responses:
"200":
description: Successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/NotificationsTimelineResponse"
tags:
- "tweet"
components: components:
schemas: schemas:
TimelineResponse: TimelineResponse:
@ -74,6 +130,8 @@ components:
$ref: "./../response/error.yaml#/components/schemas/ErrorResponse" $ref: "./../response/error.yaml#/components/schemas/ErrorResponse"
HomeTimelineResponseData: HomeTimelineResponseData:
required:
- "home"
properties: properties:
home: home:
$ref: "#/components/schemas/HomeTimelineHome" $ref: "#/components/schemas/HomeTimelineHome"
@ -97,6 +155,8 @@ components:
$ref: "./../response/error.yaml#/components/schemas/ErrorResponse" $ref: "./../response/error.yaml#/components/schemas/ErrorResponse"
ListTweetsTimelineData: ListTweetsTimelineData:
required:
- "list"
properties: properties:
list: list:
$ref: "#/components/schemas/ListTweetsTimelineList" $ref: "#/components/schemas/ListTweetsTimelineList"
@ -106,12 +166,7 @@ components:
- "tweets_timeline" - "tweets_timeline"
properties: properties:
tweets_timeline: tweets_timeline:
$ref: "#/components/schemas/ListTweetsTimeline" $ref: "#/components/schemas/TimelineResult"
ListTweetsTimeline:
properties:
timeline:
$ref: "./../schemas/timeline.yaml#/components/schemas/Timeline"
SearchTimelineResponse: SearchTimelineResponse:
required: required:
@ -125,6 +180,8 @@ components:
$ref: "./../response/error.yaml#/components/schemas/ErrorResponse" $ref: "./../response/error.yaml#/components/schemas/ErrorResponse"
SearchTimelineData: SearchTimelineData:
required:
- "search_by_raw_query"
properties: properties:
search_by_raw_query: search_by_raw_query:
$ref: "#/components/schemas/SearchByRawQuery" $ref: "#/components/schemas/SearchByRawQuery"
@ -142,3 +199,155 @@ components:
properties: properties:
timeline: timeline:
$ref: "./../schemas/timeline.yaml#/components/schemas/Timeline" $ref: "./../schemas/timeline.yaml#/components/schemas/Timeline"
CommunityTweetsTimelineResponse:
required:
- "data"
properties:
data:
$ref: "#/components/schemas/RankedCommunityTweetData"
errors:
type: array
items:
$ref: "./../response/error.yaml#/components/schemas/ErrorResponse"
RankedCommunityTweetData:
required:
- "communityResults"
properties:
communityResults:
$ref: "#/components/schemas/RankedCommunityResults"
RankedCommunityResults:
required:
- "result"
properties:
result:
$ref: "#/components/schemas/RankedCommunityResult"
RankedCommunityResult:
required:
- "__typename"
- "ranked_community_timeline"
properties:
__typename:
$ref: "./../schemas/typename.yaml#/components/schemas/TypeName" # Community
ranked_community_timeline:
$ref: "./../schemas/timeline.yaml#/components/schemas/TimelineResult"
CommunityMediaTimelineResponse:
required:
- "data"
properties:
data:
$ref: "#/components/schemas/MediaCommunityTweetData"
errors:
type: array
items:
$ref: "./../response/error.yaml#/components/schemas/ErrorResponse"
MediaCommunityTweetData:
required:
- "__typename"
- "communityResults"
properties:
communityResults:
$ref: "#/components/schemas/MediaCommunityResults"
MediaCommunityResults:
required:
- "result"
properties:
result:
$ref: "#/components/schemas/MediaCommunityResult"
MediaCommunityResult:
required:
- "__typename"
- "community_media_timeline"
properties:
__typename:
$ref: "./../schemas/typename.yaml#/components/schemas/TypeName" # Community
community_media_timeline:
$ref: "./../schemas/timeline.yaml#/components/schemas/TimelineResult"
CommunityAboutTimelineResponse:
required:
- "data"
properties:
data:
$ref: "#/components/schemas/AboutCommunityTweetData"
errors:
type: array
items:
$ref: "./../response/error.yaml#/components/schemas/ErrorResponse"
AboutCommunityTweetData:
required:
- "communityResults"
properties:
communityResults:
$ref: "#/components/schemas/AboutCommunityResults"
AboutCommunityResults:
required:
- "result"
properties:
result:
$ref: "#/components/schemas/AboutCommunityResult"
AboutCommunityResult:
required:
- "__typename"
- "about_timeline"
properties:
__typename:
$ref: "./../schemas/typename.yaml#/components/schemas/TypeName" # Community
about_timeline:
$ref: "./../schemas/timeline.yaml#/components/schemas/TimelineResult"
NotificationsTimelineResponse:
required:
- "data"
properties:
data:
$ref: "#/components/schemas/NotificationsTimelineData"
errors:
type: array
items:
$ref: "./../response/error.yaml#/components/schemas/ErrorResponse"
NotificationsTimelineData:
required:
- "viewer_v2"
properties:
viewer_v2:
$ref: "#/components/schemas/NotificationsViewerV2"
NotificationsViewerV2:
required:
- "user_results"
properties:
user_results:
$ref: "#/components/schemas/NotificationsUserResults"
NotificationsUserResults:
required:
- "result"
properties:
result:
$ref: "#/components/schemas/NotificationsResult"
NotificationsResult:
required:
- "__typename"
- "rest_id"
- "notification_timeline"
properties:
__typename:
$ref: "./../schemas/typename.yaml#/components/schemas/TypeName" # User
rest_id:
type: string
pattern: "^[0-9]+$"
notification_timeline:
$ref: "./../schemas/timeline.yaml#/components/schemas/TimelineResult"

View file

@ -108,7 +108,7 @@ components:
TweetFavoritersResponseData: TweetFavoritersResponseData:
properties: properties:
favoriters_timeline: favoriters_timeline:
$ref: "./../schemas/timeline.yaml#/components/schemas/TimelineV2" $ref: "./../schemas/timeline.yaml#/components/schemas/TimelineResult"
TweetRetweetersResponse: TweetRetweetersResponse:
required: required:
@ -124,4 +124,4 @@ components:
TweetRetweetersResponseData: TweetRetweetersResponseData:
properties: properties:
retweeters_timeline: retweeters_timeline:
$ref: "./../schemas/timeline.yaml#/components/schemas/TimelineV2" $ref: "./../schemas/timeline.yaml#/components/schemas/TimelineResult"

View file

@ -97,9 +97,19 @@ components:
- "result" - "result"
properties: properties:
result: result:
$ref: "#/components/schemas/UserTweetsResult" $ref: "#/components/schemas/UserTweetsResultV1"
UserTweetsResult: UserTweetsResultV1:
required:
- "__typename"
- "timeline"
properties:
__typename:
$ref: "./../schemas/typename.yaml#/components/schemas/TypeName" # User
timeline:
$ref: "./../schemas/timeline.yaml#/components/schemas/TimelineResult"
UserTweetsResultV2:
required: required:
- "__typename" - "__typename"
- "timeline_v2" - "timeline_v2"
@ -107,7 +117,7 @@ components:
__typename: __typename:
$ref: "./../schemas/typename.yaml#/components/schemas/TypeName" # User $ref: "./../schemas/typename.yaml#/components/schemas/TypeName" # User
timeline_v2: timeline_v2:
$ref: "./../schemas/timeline.yaml#/components/schemas/TimelineV2" $ref: "./../schemas/timeline.yaml#/components/schemas/TimelineResult"
UserHighlightsTweetsResponse: UserHighlightsTweetsResponse:
required: required:

View file

@ -51,7 +51,7 @@ components:
- "entryType" - "entryType"
- "displayType" - "displayType"
# - "items" # - "items"
- "clientEventInfo" # - "clientEventInfo"
properties: properties:
__typename: __typename:
type: string type: string
@ -126,7 +126,7 @@ components:
properties: properties:
entryId: entryId:
type: string type: string
pattern: "^(([a-z]+|[0-9]+|[0-9a-f]+)(-|$))+" pattern: "^(([a-zA-Z]+|[0-9]+|[0-9a-f]+)(-|$))+"
item: item:
$ref: "#/components/schemas/ModuleEntry" $ref: "#/components/schemas/ModuleEntry"
dispensable: dispensable:
@ -166,6 +166,7 @@ components:
- $ref: "#/components/schemas/TimelineCommunity" - $ref: "#/components/schemas/TimelineCommunity"
- $ref: "#/components/schemas/TimelineTombstone" - $ref: "#/components/schemas/TimelineTombstone"
- $ref: "#/components/schemas/TimelineTrend" - $ref: "#/components/schemas/TimelineTrend"
- $ref: "#/components/schemas/TimelineNotification"
discriminator: discriminator:
propertyName: __typename propertyName: __typename
@ -178,6 +179,7 @@ components:
TimelineCommunity: "#/components/schemas/TimelineCommunity" TimelineCommunity: "#/components/schemas/TimelineCommunity"
TimelineTombstone: "#/components/schemas/TimelineTombstone" TimelineTombstone: "#/components/schemas/TimelineTombstone"
TimelineTrend: "#/components/schemas/TimelineTrend" TimelineTrend: "#/components/schemas/TimelineTrend"
TimelineNotification: "#/components/schemas/TimelineNotification"
ContentItemType: ContentItemType:
type: string type: string
@ -190,6 +192,8 @@ components:
TimelineMessagePrompt, TimelineMessagePrompt,
TimelineCommunity, TimelineCommunity,
TimelineTombstone, TimelineTombstone,
TimelineTrend,
TimelineNotification,
] ]
TimelineTweet: TimelineTweet:
@ -233,7 +237,7 @@ components:
$ref: "#/components/schemas/SocialContextUnion" $ref: "#/components/schemas/SocialContextUnion"
userDisplayType: userDisplayType:
type: string type: string
enum: [User, UserDetailed, SubscribableUser] enum: [User, UserDetailed, SubscribableUser, UserConcise]
user_results: user_results:
$ref: "./user.yaml#/components/schemas/UserResults" $ref: "./user.yaml#/components/schemas/UserResults"
@ -276,10 +280,16 @@ components:
- "Conversation" - "Conversation"
- "List" - "List"
- "Community" - "Community"
- "Facepile"
text: text:
type: string type: string
landingUrl: landingUrl:
$ref: "#/components/schemas/SocialContextLandingUrl" $ref: "#/components/schemas/SocialContextLandingUrl"
contextImageUrls:
type: array
items:
type: string
format: uri
TimelineTopicContext: TimelineTopicContext:
type: object type: object
@ -394,7 +404,7 @@ components:
element: element:
type: string type: string
# august-2023-privacy-prompt-candidate # august-2023-privacy-prompt-candidate
# pattern: "(([a-z]+|[0-9]+|[0-9a-f]+)(-|$))+" # pattern: "^(([a-zA-Z]+|[0-9]+|[0-9a-f]+)(-|$))+"
# pattern: "^(january|february|march|april|may|june|july|august|september|october|november|december)-[0-9]{4}-([a-z]-)+[a-z]+$" # pattern: "^(january|february|march|april|may|june|july|august|september|october|november|december)-[0-9]{4}-([a-z]-)+[a-z]+$"
details: details:
type: object type: object
@ -458,12 +468,9 @@ components:
TimelineTrend: TimelineTrend:
required: required:
- "__typename" - "__typename"
- "itemType"
- "name" - "name"
- "trend_url" - "trend_url"
- "trend_metadata" - "trend_metadata"
- "thumbnail_image"
- "images"
properties: properties:
__typename: __typename:
$ref: "./typename.yaml#/components/schemas/TypeName" # TimelineTrend $ref: "./typename.yaml#/components/schemas/TypeName" # TimelineTrend
@ -509,3 +516,53 @@ components:
url: url:
type: string type: string
format: uri format: uri
TimelineNotification:
required:
- "__typename"
- "itemType"
- "id"
- "notification_icon"
- "rich_message"
- "notification_url"
- "template"
- "timestamp_ms"
properties:
__typename:
$ref: "./typename.yaml#/components/schemas/TypeName" # TimelineNotification
itemType:
$ref: "#/components/schemas/ContentItemType" # TimelineNotification
id:
type: string
notification_icon:
type: string # enum milestone_icon
rich_message:
$ref: "#/components/schemas/RichMessage"
notification_url:
$ref: "#/components/schemas/SocialContextLandingUrl"
template:
$ref: "#/components/schemas/NotificationTemplate"
timestamp_ms:
type: string # 2025-05-05T01:18:21.657Z
RichMessage:
type: object
properties:
rtl:
type: boolean
text:
type: string
NotificationTemplate:
type: object
properties:
__typename:
$ref: "./typename.yaml#/components/schemas/TypeName" # NotificationTemplate
target_objects:
type: array
items:
type: object
from_users:
type: array
items:
type: object

View file

@ -16,6 +16,8 @@ components:
- $ref: "#/components/schemas/TimelineShowAlert" - $ref: "#/components/schemas/TimelineShowAlert"
- $ref: "#/components/schemas/TimelineTerminateTimeline" - $ref: "#/components/schemas/TimelineTerminateTimeline"
- $ref: "#/components/schemas/TimelineShowCover" - $ref: "#/components/schemas/TimelineShowCover"
- $ref: "#/components/schemas/TimelineClearEntriesUnreadState"
- $ref: "#/components/schemas/TimelineMarkEntriesUnreadGreaterThanSortIndex"
discriminator: discriminator:
propertyName: type propertyName: type
@ -28,6 +30,8 @@ components:
TimelineShowAlert: "#/components/schemas/TimelineShowAlert" TimelineShowAlert: "#/components/schemas/TimelineShowAlert"
TimelineTerminateTimeline: "#/components/schemas/TimelineTerminateTimeline" TimelineTerminateTimeline: "#/components/schemas/TimelineTerminateTimeline"
TimelineShowCover: "#/components/schemas/TimelineShowCover" TimelineShowCover: "#/components/schemas/TimelineShowCover"
TimelineClearEntriesUnreadState: "#/components/schemas/TimelineClearEntriesUnreadState"
TimelineMarkEntriesUnreadGreaterThanSortIndex: "#/components/schemas/TimelineMarkEntriesUnreadGreaterThanSortIndex"
InstructionType: InstructionType:
type: string type: string
@ -41,6 +45,8 @@ components:
TimelineShowAlert, TimelineShowAlert,
TimelineTerminateTimeline, TimelineTerminateTimeline,
TimelineShowCover, TimelineShowCover,
TimelineClearEntriesUnreadState,
TimelineMarkEntriesUnreadGreaterThanSortIndex,
] ]
TimelineAddEntries: TimelineAddEntries:
@ -162,7 +168,7 @@ components:
$ref: "./content.yaml#/components/schemas/ContentUnion" $ref: "./content.yaml#/components/schemas/ContentUnion"
entryId: entryId:
type: string type: string
pattern: "^(([a-z]+|[0-9]+|[0-9a-f]+)(-|$))+" pattern: "^(([a-zA-Z]+|[0-9]+|[0-9a-f]+)(-|$))+"
sortIndex: sortIndex:
type: string type: string
pattern: "[0-9]+$" pattern: "[0-9]+$"
@ -309,3 +315,20 @@ components:
action: action:
type: string type: string
enum: [primary_cta] enum: [primary_cta]
TimelineClearEntriesUnreadState:
required:
- type
properties:
type:
$ref: "#/components/schemas/InstructionType" # TimelineClearEntriesUnreadState
TimelineMarkEntriesUnreadGreaterThanSortIndex:
required:
- type
properties:
type:
$ref: "#/components/schemas/InstructionType" # TimelineMarkEntriesUnreadGreaterThanSortIndex
sort_index:
type: string
pattern: "[0-9]+$"

View file

@ -6,8 +6,10 @@ info:
paths: {} paths: {}
components: components:
schemas: schemas:
TimelineV2: TimelineResult:
properties: properties:
id:
type: string
timeline: timeline:
$ref: "#/components/schemas/Timeline" $ref: "#/components/schemas/Timeline"

View file

@ -709,6 +709,8 @@ components:
enum: [BirdwatchV1Icon] enum: [BirdwatchV1Icon]
callToAction: callToAction:
$ref: "#/components/schemas/BirdwatchPivotCallToAction" $ref: "#/components/schemas/BirdwatchPivotCallToAction"
titleDetail:
type: string
BirdwatchPivotFooter: BirdwatchPivotFooter:
required: required:
@ -1549,6 +1551,18 @@ components:
properties: properties:
media_key: media_key:
type: string type: string
grok_image_annotation:
$ref: "#/components/schemas/GrokImageAnnotation"
GrokImageAnnotation:
required:
- "prompt"
- "upsampled_prompt"
properties:
prompt:
type: string
upsampled_prompt:
type: string
TrendResults: TrendResults:
required: required:
@ -1570,7 +1584,7 @@ components:
GrokShareAttachmentItem: GrokShareAttachmentItem:
required: required:
- "media_urls" - "media_urls"
- message - "message"
properties: properties:
media_urls: media_urls:
type: array type: array
@ -1579,6 +1593,15 @@ components:
format: uri format: uri
message: message:
type: string type: string
analysis_post_id_results:
$ref: "#/components/schemas/AnalysisResults"
AnalysisResults:
required:
- "result"
properties:
result:
$ref: "#/components/schemas/Tweet"
TweetPreviewDisplay: TweetPreviewDisplay:
required: required:

View file

@ -22,6 +22,8 @@ components:
TimelineCommunity, TimelineCommunity,
TimelineTombstone, TimelineTombstone,
TimelineTrend, TimelineTrend,
TimelineNotification,
TimelineNotificationAggregateUserActions,
TweetUnavailable, TweetUnavailable,
TweetPreviewDisplay, TweetPreviewDisplay,
Tweet, Tweet,

View file

@ -95,7 +95,7 @@ components:
type: boolean type: boolean
parody_commentary_fan_label: parody_commentary_fan_label:
type: string type: string
enum: ["None", "Parody"] enum: ["None", "Parody", "Commentary"]
UserProfessional: UserProfessional:
required: required:

View file

@ -12,8 +12,16 @@ from enum import Enum
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
import bs4
import openapi_client as pt import openapi_client as pt
import requests
import urllib3 import urllib3
from x_client_transaction import ClientTransaction
from x_client_transaction.utils import (
generate_headers,
get_ondemand_file_url,
handle_x_migration,
)
warnings.filterwarnings("ignore") warnings.filterwarnings("ignore")
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)s %(message)s") logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)s %(message)s")
@ -57,14 +65,35 @@ def find_name(x):
return [x["name"]] return [x["name"]]
def get_transaction_base():
session = requests.Session()
session.headers = generate_headers()
home_page_response = handle_x_migration(session=session)
home_page = session.get(url="https://x.com")
home_page_response = bs4.BeautifulSoup(home_page.content, "html.parser")
ondemand_file_url = get_ondemand_file_url(response=home_page_response)
ondemand_file = session.get(url=ondemand_file_url)
ondemand_file_response = bs4.BeautifulSoup(ondemand_file.content, "html.parser")
ct = ClientTransaction(home_page_response, ondemand_file_response)
return ct
def get_transaction_id(key, ct=get_transaction_base()):
return ct.generate_transaction_id(
method=placeholder[key]["@method"], path=placeholder[key]["@path"]
)
def get_kwargs(key, additional): def get_kwargs(key, additional):
kwargs = {"path_query_id": placeholder[key]["queryId"]} kwargs = {"path_query_id": placeholder[key]["queryId"], "_headers": {}}
if placeholder[key].get("variables") is not None: if placeholder[key].get("variables") is not None:
kwargs["variables"] = json.dumps(placeholder[key]["variables"] | additional) kwargs["variables"] = json.dumps(placeholder[key]["variables"] | additional)
if placeholder[key].get("features") is not None: if placeholder[key].get("features") is not None:
kwargs["features"] = json.dumps(placeholder[key]["features"]) kwargs["features"] = json.dumps(placeholder[key]["features"])
if placeholder[key].get("fieldToggles") is not None: if placeholder[key].get("fieldToggles") is not None:
kwargs["field_toggles"] = json.dumps(placeholder[key]["fieldToggles"]) kwargs["field_toggles"] = json.dumps(placeholder[key]["fieldToggles"])
if placeholder[key].get("@path") is not None:
kwargs["_headers"]["x-client-transaction-id"] = get_transaction_id(key)
return kwargs return kwargs
@ -267,6 +296,10 @@ if __name__ == "__main__":
api_conf.access_token = access_token api_conf.access_token = access_token
api_client = pt.ApiClient(configuration=api_conf, cookie=cookies_str) api_client = pt.ApiClient(configuration=api_conf, cookie=cookies_str)
api_client.user_agent = get_header(latest_user_agent, "chrome-fetch")["user-agent"] api_client.user_agent = get_header(latest_user_agent, "chrome-fetch")["user-agent"]
session = requests.Session()
session.headers = get_header(latest_user_agent, "chrome")
error_count = 0 error_count = 0
for x in [pt.DefaultApi, pt.TweetApi, pt.UserApi, pt.UsersApi, pt.UserListApi]: for x in [pt.DefaultApi, pt.TweetApi, pt.UserApi, pt.UsersApi, pt.UserListApi]:
@ -388,7 +421,7 @@ if __name__ == "__main__":
"1875050002046726519", "1875050002046726519",
"1848219562136801480", "1848219562136801480",
"1881993128288399684", "1881993128288399684",
"1899104692577489182" "1899104692577489182",
] ]
for id in ids: for id in ids:
try: try:

View file

@ -3,65 +3,77 @@ import re
from urllib.parse import urlencode, urlparse from urllib.parse import urlencode, urlparse
import openapi_client as pt import openapi_client as pt
import requests
import urllib3 import urllib3
from x_client_transaction import ClientTransaction
from x_client_transaction.utils import handle_x_migration
def get_kwargs(key, additional): def get_kwargs(key, additional):
kwargs = {"path_query_id": placeholder[key]["queryId"]} kwargs = {"path_query_id": placeholder[key]["queryId"], "_headers": {}}
if placeholder[key].get("variables") is not None: if placeholder[key].get("variables") is not None:
kwargs["variables"] = json.dumps(placeholder[key]["variables"] | additional) kwargs["variables"] = json.dumps(placeholder[key]["variables"] | additional)
if placeholder[key].get("features") is not None: if placeholder[key].get("features") is not None:
kwargs["features"] = json.dumps(placeholder[key]["features"]) kwargs["features"] = json.dumps(placeholder[key]["features"])
if placeholder[key].get("fieldToggles") is not None: if placeholder[key].get("fieldToggles") is not None:
kwargs["field_toggles"] = json.dumps(placeholder[key]["fieldToggles"]) kwargs["field_toggles"] = json.dumps(placeholder[key]["fieldToggles"])
if placeholder[key].get("@path") is not None:
kwargs["_headers"]["x-client-transaction-id"] = ct.generate_transaction_id(
method=placeholder[key]["@method"], path=placeholder[key]["@path"]
)
return kwargs return kwargs
class SessionManager: class SessionManager:
def __init__(self) -> None: def __init__(self) -> None:
header = "https://raw.githubusercontent.com/fa0311/latest-user-agent/refs/heads/main/header.json" header = "https://raw.githubusercontent.com/fa0311/latest-user-agent/refs/heads/main/header.json"
self.http = urllib3.PoolManager() self.http = urllib3.PoolManager()
self.chorome_header = json.loads(self.http.request("GET", header).data) self.chorome_header = json.loads(self.http.request("GET", header).data)
def child(self): def child(self):
return SessionManagerChild(self.http,self.chorome_header) return SessionManagerChild(self.http, self.chorome_header)
class SessionManagerChild: class SessionManagerChild:
def __init__(self, http, chorome_header) -> None: def __init__(self, http, chorome_header) -> None:
self.http = http self.http = http
self.chorome_header = chorome_header self.chorome_header = chorome_header
self.session = {} self.session = {}
def cookie_normalize(self, cookie: list[str]) -> dict[str, str]: def cookie_normalize(self, cookie: list[str]) -> dict[str, str]:
value = {x.split("; ")[0].split("=")[0]: x.split("; ")[0].split("=")[1] for x in cookie} value = {
x.split("; ")[0].split("=")[0]: x.split("; ")[0].split("=")[1]
for x in cookie
}
return {key: value[key] for key in value if len(value[key]) > 0} return {key: value[key] for key in value if len(value[key]) > 0}
def cookie_to_str(self, cookie: dict[str, str]) -> str: def cookie_to_str(self, cookie: dict[str, str]) -> str:
return "; ".join([f"{key}={value}" for key, value in cookie.items()]) return "; ".join([f"{key}={value}" for key, value in cookie.items()])
def getHader(self, additionals={}) -> dict[str, str]: def getHader(self, additionals={}) -> dict[str, str]:
ignore = ["host", "connection"] ignore = ["host", "connection"]
base = {key: value for key, value in self.chorome_header["chrome"].items() if key not in ignore} base = {
key: value
for key, value in self.chorome_header["chrome"].items()
if key not in ignore
}
return base | {"cookie": self.cookie_to_str(self.session)} | additionals return base | {"cookie": self.cookie_to_str(self.session)} | additionals
def update_normalize(self, cookie: list[str]): def update_normalize(self, cookie: list[str]):
self.update(self.cookie_normalize(cookie)) self.update(self.cookie_normalize(cookie))
def update(self, cookie: dict[str, str]): def update(self, cookie: dict[str, str]):
self.session.update(cookie) self.session.update(cookie)
def pop(self, key: str): def pop(self, key: str):
self.session.pop(key) self.session.pop(key)
def get(self, key: str): def get(self, key: str):
return self.session.get(key) return self.session.get(key)
def to_str(self): def to_str(self):
return self.cookie_to_str(self.session) return self.cookie_to_str(self.session)
def get_guest_token(): def get_guest_token():
twitter_url = "https://x.com/elonmusk" twitter_url = "https://x.com/elonmusk"
@ -69,9 +81,7 @@ def get_guest_token():
chrome = SessionManager() chrome = SessionManager()
x = chrome.child() x = chrome.child()
twitter = chrome.child() twitter = chrome.child()
def regex(str: str, **kwargs) -> str: def regex(str: str, **kwargs) -> str:
return str.format( return str.format(
quote=r"[\'\"]", quote=r"[\'\"]",
@ -79,21 +89,30 @@ def get_guest_token():
dot=r"\.", dot=r"\.",
any=r".*?", any=r".*?",
target=r"([\s\S]*?)", target=r"([\s\S]*?)",
**kwargs **kwargs,
) )
def redirect(method: str, url: str, body: str = None, headers: dict[str, str] = {}) -> urllib3.HTTPResponse: def redirect(
method: str, url: str, body: str = None, headers: dict[str, str] = {}
) -> urllib3.HTTPResponse:
for _ in range(10): for _ in range(10):
if urlparse(url).netloc == "x.com": if urlparse(url).netloc == "x.com":
res = http.request(method, url, headers=x.getHader(headers), body=body, redirect=False) res = http.request(
method, url, headers=x.getHader(headers), body=body, redirect=False
)
x.update_normalize(res.headers._container["set-cookie"][1:]) x.update_normalize(res.headers._container["set-cookie"][1:])
elif urlparse(url).netloc == "twitter.com": elif urlparse(url).netloc == "twitter.com":
res = http.request(method, url, headers=twitter.getHader(headers), body=body, redirect=False) res = http.request(
method,
url,
headers=twitter.getHader(headers),
body=body,
redirect=False,
)
twitter.update_normalize(res.headers._container["set-cookie"][1:]) twitter.update_normalize(res.headers._container["set-cookie"][1:])
else: else:
raise Exception("Invalid domain") raise Exception("Invalid domain")
method = "GET" method = "GET"
body = None body = None
headers = {} headers = {}
@ -109,7 +128,7 @@ def get_guest_token():
url = f"{domain}{new_path}" url = f"{domain}{new_path}"
else: else:
url = new_path url = new_path
elif re.findall(regex(location), res.data.decode()): elif re.findall(regex(location), res.data.decode()):
url = re.findall(regex(location), res.data.decode())[0] url = re.findall(regex(location), res.data.decode())[0]
elif re.findall(regex(submit), res.data.decode()): elif re.findall(regex(submit), res.data.decode()):
@ -118,7 +137,7 @@ def get_guest_token():
input_html = re.findall(regex(input), form_html[0][1]) input_html = re.findall(regex(input), form_html[0][1])
method = "POST" method = "POST"
url = form_html[0][0] url = form_html[0][0]
body = urlencode({k:v for k,v in input_html}) body = urlencode({k: v for k, v in input_html})
headers = {"content-type": "application/x-www-form-urlencoded"} headers = {"content-type": "application/x-www-form-urlencoded"}
elif res.status == 200: elif res.status == 200:
return res return res
@ -126,21 +145,19 @@ def get_guest_token():
raise Exception("Failed to redirect") raise Exception("Failed to redirect")
else: else:
raise Exception("Failed to redirect") raise Exception("Failed to redirect")
res = redirect("GET", twitter_url) res = redirect("GET", twitter_url)
reg = "document{dot}cookie{space}={space}{quote}{target}{quote}" reg = "document{dot}cookie{space}={space}{quote}{target}{quote}"
if re.findall(regex(reg), res.data.decode()): if re.findall(regex(reg), res.data.decode()):
find = re.findall(regex(reg), res.data.decode()) find = re.findall(regex(reg), res.data.decode())
x.update_normalize(find) x.update_normalize(find)
if x.get("gt") is None: if x.get("gt") is None:
raise Exception("Failed to get guest token") raise Exception("Failed to get guest token")
return x return x
if __name__ == "__main__": if __name__ == "__main__":
cookies = get_guest_token() cookies = get_guest_token()
cookies_str = cookies.to_str() cookies_str = cookies.to_str()
@ -167,15 +184,33 @@ if __name__ == "__main__":
api_client = pt.ApiClient(configuration=api_conf, cookie=cookies_str) api_client = pt.ApiClient(configuration=api_conf, cookie=cookies_str)
api_client.user_agent = latest_user_agent["chrome-fetch"] api_client.user_agent = latest_user_agent["chrome-fetch"]
res = pt.TweetApi(api_client).get_user_tweets_with_http_info( session = requests.Session()
**get_kwargs("UserTweets", {}), session.headers = latest_user_agent["chrome"]
).model_dump_json() ct = ClientTransaction(handle_x_migration(session))
res = pt.TweetApi(api_client).get_user_highlights_tweets_with_http_info(
**get_kwargs("UserHighlightsTweets", {}), res = (
).model_dump_json() pt.TweetApi(api_client)
res = pt.DefaultApi(api_client).get_tweet_result_by_rest_id_with_http_info( .get_user_tweets_with_http_info(
**get_kwargs("TweetResultByRestId", {}), **get_kwargs("UserTweets", {}),
).model_dump_json() )
res = pt.UserApi(api_client).get_user_by_screen_name_with_http_info( .model_dump_json()
**get_kwargs("UserByScreenName", {}) )
).model_dump_json() res = (
pt.TweetApi(api_client)
.get_user_highlights_tweets_with_http_info(
**get_kwargs("UserHighlightsTweets", {}),
)
.model_dump_json()
)
res = (
pt.DefaultApi(api_client)
.get_tweet_result_by_rest_id_with_http_info(
**get_kwargs("TweetResultByRestId", {}),
)
.model_dump_json()
)
res = (
pt.UserApi(api_client)
.get_user_by_screen_name_with_http_info(**get_kwargs("UserByScreenName", {}))
.model_dump_json()
)

View file

@ -18,14 +18,14 @@ class Config:
getParamHook = AddParametersOnContent( getParamHook = AddParametersOnContent(
split=-1, split=-1,
contentType="application/json", contentType="application/json",
ignoreKeys=["queryId"], ignoreKeys=["queryId", "@path", "@method"],
) )
else: else:
# ["parameters"][0]["schema"] # ["parameters"][0]["schema"]
getParamHook = AddParametersOnParameters( getParamHook = AddParametersOnParameters(
split=-1, split=-1,
schemaType="string", schemaType="string",
ignoreKeys=["queryId"], ignoreKeys=["queryId", "@path", "@method"],
) )
return { return {

View file

@ -143,6 +143,8 @@ def main():
with open("./src/config/placeholder.json", "w") as f: with open("./src/config/placeholder.json", "w") as f:
placeholder[endpoint] = placeholder.get(endpoint, {}) placeholder[endpoint] = placeholder.get(endpoint, {})
placeholder[endpoint]["@path"] = f"/i/api/graphql/{query_id}/{endpoint}"
placeholder[endpoint]["@method"] = method
placeholder[endpoint]["queryId"] = query_id placeholder[endpoint]["queryId"] = query_id
if features: if features:
placeholder[endpoint]["features"] = features placeholder[endpoint]["features"] = features