diff --git a/.gitignore b/.gitignore index 5e63e49..a7e4882 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ __pycache__/ cookie.json *_generated -*.jar \ No newline at end of file +*.jar +cache/ \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index e5d109e..1ffce76 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -6,8 +6,10 @@ "type": "shell", "linux": { "command": [ + "source .venv/bin/activate;", "python3 tools/build.py;", "java -jar openapi-generator-cli.jar generate -c test/python/openapi-generator-config.yaml -g python;", + "python3 test/python/replace.py;", "python3 -m pip install ./python_generated;" ] }, @@ -16,6 +18,7 @@ ".venv/Scripts/activate;", "python tools/build.py;", "java -jar openapi-generator-cli.jar generate -c test/python/openapi-generator-config.yaml -g python;", + "python test/python/replace.py;", "python -m pip install ./python_generated;" ] } diff --git a/dist/compatible/paths/other.yaml b/dist/compatible/paths/other.yaml index 33e5aa9..1f8cae6 100644 --- a/dist/compatible/paths/other.yaml +++ b/dist/compatible/paths/other.yaml @@ -47,6 +47,8 @@ components: superFollowersCount: type: integer superFollowsApplicationStatus: + enum: + - NotStarted type: string userFeatures: $ref: '#/components/schemas/UserFeatures' diff --git a/dist/compatible/schemas/content.yaml b/dist/compatible/schemas/content.yaml index cf53767..ccadf95 100644 --- a/dist/compatible/schemas/content.yaml +++ b/dist/compatible/schemas/content.yaml @@ -138,6 +138,9 @@ components: additionalProperties: true type: object displayType: + enum: + - Vertical + - VerticalConversation type: string entryType: $ref: '#/components/schemas/ContentEntryType' @@ -185,6 +188,10 @@ components: itemType: $ref: '#/components/schemas/ContentItemType' userDisplayType: + enum: + - User + - UserDetailed + - SubscribableUser type: string user_results: $ref: ./user.yaml#/components/schemas/UserResults diff --git a/dist/compatible/schemas/tweet.yaml b/dist/compatible/schemas/tweet.yaml index bea1c3c..454ee82 100644 --- a/dist/compatible/schemas/tweet.yaml +++ b/dist/compatible/schemas/tweet.yaml @@ -1,5 +1,11 @@ components: schemas: + AdditionalMediaInfo: + properties: + monetizable: + type: boolean + required: + - monetizable Entities: properties: hashtags: @@ -31,7 +37,7 @@ components: properties: media: items: - $ref: '#/components/schemas/Media' + $ref: '#/components/schemas/MediaExtended' type: array required: - media @@ -46,8 +52,7 @@ components: expanded_url: format: uri type: string - ext_media_availability: - additionalProperties: true + features: type: object id_str: pattern: ^[0-9]+$ @@ -56,42 +61,180 @@ components: items: type: integer type: array - media_key: - pattern: ^[0-9]+_[0-9]+$ - type: string media_url_https: format: uri type: string original_info: - properties: - focus_rects: - items: - additionalProperties: true - type: object - type: array - height: - type: integer - width: - type: integer - type: object + $ref: '#/components/schemas/MediaOriginalInfo' sizes: - additionalProperties: true - type: object + $ref: '#/components/schemas/MediaSizes' type: + enum: + - photo + - video + - animated_gif type: string url: format: uri type: string required: + - display_url + - expanded_url - id_str - indices - media_url_https - - url - - display_url - - expanded_url - type + - url - sizes - original_info + MediaExtended: + properties: + additional_media_info: + $ref: '#/components/schemas/AdditionalMediaInfo' + display_url: + format: uri + type: string + expanded_url: + format: uri + type: string + ext_media_availability: + $ref: '#/components/schemas/extMediaAvailability' + features: + type: object + id_str: + pattern: ^[0-9]+$ + type: string + indices: + items: + type: integer + type: array + mediaStats: + $ref: '#/components/schemas/mediaStats' + media_key: + type: string + media_url_https: + format: uri + type: string + original_info: + $ref: '#/components/schemas/MediaOriginalInfo' + sizes: + $ref: '#/components/schemas/MediaSizes' + type: + enum: + - photo + - video + - animated_gif + type: string + url: + format: uri + type: string + video_info: + $ref: '#/components/schemas/MediaVideoInfo' + required: + - display_url + - expanded_url + - id_str + - indices + - media_key + - media_url_https + - type + - url + - ext_media_availability + - sizes + - original_info + MediaOriginalInfo: + properties: + focus_rects: + items: + $ref: '#/components/schemas/MediaOriginalInfoFocusRect' + type: array + height: + type: integer + width: + type: integer + required: + - height + - width + MediaOriginalInfoFocusRect: + properties: + h: + type: integer + w: + type: integer + x: + type: integer + y: + type: integer + required: + - x + - y + - w + - h + type: object + MediaSize: + properties: + h: + type: integer + resize: + enum: + - crop + - fit + type: string + w: + type: integer + required: + - w + - h + - resize + MediaSizes: + properties: + large: + $ref: '#/components/schemas/MediaSize' + medium: + $ref: '#/components/schemas/MediaSize' + small: + $ref: '#/components/schemas/MediaSize' + thumb: + $ref: '#/components/schemas/MediaSize' + required: + - large + - medium + - small + - thumb + MediaVideoInfo: + properties: + aspect_ratio: + items: + type: integer + type: array + duration_millis: + type: integer + variants: + items: + $ref: '#/components/schemas/MediaVideoInfoVariant' + type: array + required: + - aspect_ratio + - variants + MediaVideoInfoVariant: + properties: + bitrate: + type: integer + content_type: + type: string + url: + format: uri + type: string + required: + - content_type + - url + SelfThread: + properties: + id_str: + pattern: ^[0-9]+$ + type: string + required: + - id_str Symbol: additionalProperties: true type: object @@ -100,69 +243,13 @@ components: __typename: $ref: ./typename.yaml#/components/schemas/TypeName card: - properties: - legacy: - properties: - binding_values: - items: - properties: - key: - type: string - value: - properties: - boolean_value: - type: boolean - scribe_key: - type: string - string_value: - type: string - type: - type: string - required: - - type - type: object - required: - - key - - value - type: object - type: array - name: - type: string - url: - type: string - required: - - binding_values - - name - - url - type: object - rest_id: - type: string - type: object + $ref: '#/components/schemas/TweetCard' core: $ref: ./user.yaml#/components/schemas/UserResultCore edit_control: - properties: - edit_tweet_ids: - items: - pattern: ^[0-9]+$ - type: string - type: array - editable_until_msecs: - pattern: ^[0-9]+$ - type: string - edits_remaining: - pattern: ^[0-9]+$ - type: string - is_edit_eligible: - type: boolean - type: object + $ref: '#/components/schemas/TweetEditControl' edit_prespective: - properties: - favorited: - type: boolean - retweeted: - type: boolean - type: object + $ref: '#/components/schemas/TweetEditPrespective' is_translatable: default: false type: boolean @@ -173,24 +260,108 @@ components: rest_id: pattern: ^[0-9]+$ type: string + source: + type: string unmention_data: additionalProperties: true type: object views: - properties: - count: - pattern: ^[0-9]+$ - type: string - state: - type: string - type: object + $ref: '#/components/schemas/TweetView' required: - rest_id - core - edit_control - is_translatable + - source - legacy - views + TweetCard: + properties: + legacy: + $ref: '#/components/schemas/TweetCardLegacy' + rest_id: + type: string + TweetCardLegacy: + properties: + binding_values: + items: + $ref: '#/components/schemas/TweetCardLegacyBindingValue' + type: array + name: + type: string + url: + type: string + required: + - binding_values + - name + - url + TweetCardLegacyBindingValue: + properties: + key: + type: string + value: + $ref: '#/components/schemas/TweetCardLegacyBindingValueData' + required: + - key + - value + TweetCardLegacyBindingValueData: + properties: + boolean_value: + type: boolean + scribe_key: + type: string + string_value: + type: string + type: + type: string + required: + - type + TweetEditControl: + properties: + edit_control_initial: + $ref: '#/components/schemas/TweetEditControlInitial' + edit_tweet_ids: + items: + pattern: ^[0-9]+$ + type: string + type: array + editable_until_msecs: + pattern: ^[0-9]+$ + type: string + edits_remaining: + pattern: ^[0-9]+$ + type: string + initial_tweet_id: + pattern: ^[0-9]+$ + type: string + is_edit_eligible: + type: boolean + TweetEditControlInitial: + properties: + edit_tweet_ids: + items: + pattern: ^[0-9]+$ + type: string + type: array + editable_until_msecs: + pattern: ^[0-9]+$ + type: string + edits_remaining: + pattern: ^[0-9]+$ + type: string + is_edit_eligible: + type: boolean + required: + - edit_tweet_ids + - editable_until_msecs + - is_edit_eligible + - edits_remaining + TweetEditPrespective: + properties: + favorited: + type: boolean + retweeted: + type: boolean TweetLegacy: properties: bookmark_count: @@ -240,13 +411,7 @@ components: retweeted_status_result: $ref: ./content.yaml#/components/schemas/ItemResult self_thread: - properties: - id_str: - pattern: ^[0-9]+$ - type: string - required: - - id_str - type: object + $ref: '#/components/schemas/SelfThread' user_id_str: pattern: ^[0-9]+$ type: string @@ -284,6 +449,17 @@ components: - $ref: '#/components/schemas/Tweet' - $ref: '#/components/schemas/TweetWithVisibilityResults' - $ref: '#/components/schemas/TweetTombstone' + TweetView: + properties: + count: + pattern: ^[0-9]+$ + type: string + state: + enum: + - EnabledWithCount + type: string + required: + - state TweetWithVisibilityResults: properties: __typename: @@ -316,6 +492,20 @@ components: UserMention: additionalProperties: true type: object + extMediaAvailability: + properties: + status: + enum: + - Available + type: string + required: + - status + mediaStats: + properties: + viewCount: + type: integer + required: + - viewCount info: title: Twitter OpenAPI version: 0.0.1 diff --git a/dist/compatible/schemas/typename.yaml b/dist/compatible/schemas/typename.yaml index 663cb75..603a683 100644 --- a/dist/compatible/schemas/typename.yaml +++ b/dist/compatible/schemas/typename.yaml @@ -11,6 +11,7 @@ components: - TweetTombstone - TimelinePrompt - TimelineMessagePrompt + - TweetUnavailable - Tweet - User - UserUnavailable diff --git a/dist/docs/paths/other.yaml b/dist/docs/paths/other.yaml index 33e5aa9..1f8cae6 100644 --- a/dist/docs/paths/other.yaml +++ b/dist/docs/paths/other.yaml @@ -47,6 +47,8 @@ components: superFollowersCount: type: integer superFollowsApplicationStatus: + enum: + - NotStarted type: string userFeatures: $ref: '#/components/schemas/UserFeatures' diff --git a/dist/docs/schemas/content.yaml b/dist/docs/schemas/content.yaml index cf53767..ccadf95 100644 --- a/dist/docs/schemas/content.yaml +++ b/dist/docs/schemas/content.yaml @@ -138,6 +138,9 @@ components: additionalProperties: true type: object displayType: + enum: + - Vertical + - VerticalConversation type: string entryType: $ref: '#/components/schemas/ContentEntryType' @@ -185,6 +188,10 @@ components: itemType: $ref: '#/components/schemas/ContentItemType' userDisplayType: + enum: + - User + - UserDetailed + - SubscribableUser type: string user_results: $ref: ./user.yaml#/components/schemas/UserResults diff --git a/dist/docs/schemas/tweet.yaml b/dist/docs/schemas/tweet.yaml index bea1c3c..454ee82 100644 --- a/dist/docs/schemas/tweet.yaml +++ b/dist/docs/schemas/tweet.yaml @@ -1,5 +1,11 @@ components: schemas: + AdditionalMediaInfo: + properties: + monetizable: + type: boolean + required: + - monetizable Entities: properties: hashtags: @@ -31,7 +37,7 @@ components: properties: media: items: - $ref: '#/components/schemas/Media' + $ref: '#/components/schemas/MediaExtended' type: array required: - media @@ -46,8 +52,7 @@ components: expanded_url: format: uri type: string - ext_media_availability: - additionalProperties: true + features: type: object id_str: pattern: ^[0-9]+$ @@ -56,42 +61,180 @@ components: items: type: integer type: array - media_key: - pattern: ^[0-9]+_[0-9]+$ - type: string media_url_https: format: uri type: string original_info: - properties: - focus_rects: - items: - additionalProperties: true - type: object - type: array - height: - type: integer - width: - type: integer - type: object + $ref: '#/components/schemas/MediaOriginalInfo' sizes: - additionalProperties: true - type: object + $ref: '#/components/schemas/MediaSizes' type: + enum: + - photo + - video + - animated_gif type: string url: format: uri type: string required: + - display_url + - expanded_url - id_str - indices - media_url_https - - url - - display_url - - expanded_url - type + - url - sizes - original_info + MediaExtended: + properties: + additional_media_info: + $ref: '#/components/schemas/AdditionalMediaInfo' + display_url: + format: uri + type: string + expanded_url: + format: uri + type: string + ext_media_availability: + $ref: '#/components/schemas/extMediaAvailability' + features: + type: object + id_str: + pattern: ^[0-9]+$ + type: string + indices: + items: + type: integer + type: array + mediaStats: + $ref: '#/components/schemas/mediaStats' + media_key: + type: string + media_url_https: + format: uri + type: string + original_info: + $ref: '#/components/schemas/MediaOriginalInfo' + sizes: + $ref: '#/components/schemas/MediaSizes' + type: + enum: + - photo + - video + - animated_gif + type: string + url: + format: uri + type: string + video_info: + $ref: '#/components/schemas/MediaVideoInfo' + required: + - display_url + - expanded_url + - id_str + - indices + - media_key + - media_url_https + - type + - url + - ext_media_availability + - sizes + - original_info + MediaOriginalInfo: + properties: + focus_rects: + items: + $ref: '#/components/schemas/MediaOriginalInfoFocusRect' + type: array + height: + type: integer + width: + type: integer + required: + - height + - width + MediaOriginalInfoFocusRect: + properties: + h: + type: integer + w: + type: integer + x: + type: integer + y: + type: integer + required: + - x + - y + - w + - h + type: object + MediaSize: + properties: + h: + type: integer + resize: + enum: + - crop + - fit + type: string + w: + type: integer + required: + - w + - h + - resize + MediaSizes: + properties: + large: + $ref: '#/components/schemas/MediaSize' + medium: + $ref: '#/components/schemas/MediaSize' + small: + $ref: '#/components/schemas/MediaSize' + thumb: + $ref: '#/components/schemas/MediaSize' + required: + - large + - medium + - small + - thumb + MediaVideoInfo: + properties: + aspect_ratio: + items: + type: integer + type: array + duration_millis: + type: integer + variants: + items: + $ref: '#/components/schemas/MediaVideoInfoVariant' + type: array + required: + - aspect_ratio + - variants + MediaVideoInfoVariant: + properties: + bitrate: + type: integer + content_type: + type: string + url: + format: uri + type: string + required: + - content_type + - url + SelfThread: + properties: + id_str: + pattern: ^[0-9]+$ + type: string + required: + - id_str Symbol: additionalProperties: true type: object @@ -100,69 +243,13 @@ components: __typename: $ref: ./typename.yaml#/components/schemas/TypeName card: - properties: - legacy: - properties: - binding_values: - items: - properties: - key: - type: string - value: - properties: - boolean_value: - type: boolean - scribe_key: - type: string - string_value: - type: string - type: - type: string - required: - - type - type: object - required: - - key - - value - type: object - type: array - name: - type: string - url: - type: string - required: - - binding_values - - name - - url - type: object - rest_id: - type: string - type: object + $ref: '#/components/schemas/TweetCard' core: $ref: ./user.yaml#/components/schemas/UserResultCore edit_control: - properties: - edit_tweet_ids: - items: - pattern: ^[0-9]+$ - type: string - type: array - editable_until_msecs: - pattern: ^[0-9]+$ - type: string - edits_remaining: - pattern: ^[0-9]+$ - type: string - is_edit_eligible: - type: boolean - type: object + $ref: '#/components/schemas/TweetEditControl' edit_prespective: - properties: - favorited: - type: boolean - retweeted: - type: boolean - type: object + $ref: '#/components/schemas/TweetEditPrespective' is_translatable: default: false type: boolean @@ -173,24 +260,108 @@ components: rest_id: pattern: ^[0-9]+$ type: string + source: + type: string unmention_data: additionalProperties: true type: object views: - properties: - count: - pattern: ^[0-9]+$ - type: string - state: - type: string - type: object + $ref: '#/components/schemas/TweetView' required: - rest_id - core - edit_control - is_translatable + - source - legacy - views + TweetCard: + properties: + legacy: + $ref: '#/components/schemas/TweetCardLegacy' + rest_id: + type: string + TweetCardLegacy: + properties: + binding_values: + items: + $ref: '#/components/schemas/TweetCardLegacyBindingValue' + type: array + name: + type: string + url: + type: string + required: + - binding_values + - name + - url + TweetCardLegacyBindingValue: + properties: + key: + type: string + value: + $ref: '#/components/schemas/TweetCardLegacyBindingValueData' + required: + - key + - value + TweetCardLegacyBindingValueData: + properties: + boolean_value: + type: boolean + scribe_key: + type: string + string_value: + type: string + type: + type: string + required: + - type + TweetEditControl: + properties: + edit_control_initial: + $ref: '#/components/schemas/TweetEditControlInitial' + edit_tweet_ids: + items: + pattern: ^[0-9]+$ + type: string + type: array + editable_until_msecs: + pattern: ^[0-9]+$ + type: string + edits_remaining: + pattern: ^[0-9]+$ + type: string + initial_tweet_id: + pattern: ^[0-9]+$ + type: string + is_edit_eligible: + type: boolean + TweetEditControlInitial: + properties: + edit_tweet_ids: + items: + pattern: ^[0-9]+$ + type: string + type: array + editable_until_msecs: + pattern: ^[0-9]+$ + type: string + edits_remaining: + pattern: ^[0-9]+$ + type: string + is_edit_eligible: + type: boolean + required: + - edit_tweet_ids + - editable_until_msecs + - is_edit_eligible + - edits_remaining + TweetEditPrespective: + properties: + favorited: + type: boolean + retweeted: + type: boolean TweetLegacy: properties: bookmark_count: @@ -240,13 +411,7 @@ components: retweeted_status_result: $ref: ./content.yaml#/components/schemas/ItemResult self_thread: - properties: - id_str: - pattern: ^[0-9]+$ - type: string - required: - - id_str - type: object + $ref: '#/components/schemas/SelfThread' user_id_str: pattern: ^[0-9]+$ type: string @@ -284,6 +449,17 @@ components: - $ref: '#/components/schemas/Tweet' - $ref: '#/components/schemas/TweetWithVisibilityResults' - $ref: '#/components/schemas/TweetTombstone' + TweetView: + properties: + count: + pattern: ^[0-9]+$ + type: string + state: + enum: + - EnabledWithCount + type: string + required: + - state TweetWithVisibilityResults: properties: __typename: @@ -316,6 +492,20 @@ components: UserMention: additionalProperties: true type: object + extMediaAvailability: + properties: + status: + enum: + - Available + type: string + required: + - status + mediaStats: + properties: + viewCount: + type: integer + required: + - viewCount info: title: Twitter OpenAPI version: 0.0.1 diff --git a/dist/docs/schemas/typename.yaml b/dist/docs/schemas/typename.yaml index 663cb75..603a683 100644 --- a/dist/docs/schemas/typename.yaml +++ b/dist/docs/schemas/typename.yaml @@ -11,6 +11,7 @@ components: - TweetTombstone - TimelinePrompt - TimelineMessagePrompt + - TweetUnavailable - Tweet - User - UserUnavailable diff --git a/src/openapi/paths/other.yaml b/src/openapi/paths/other.yaml index 643005e..7bab3ec 100644 --- a/src/openapi/paths/other.yaml +++ b/src/openapi/paths/other.yaml @@ -68,7 +68,8 @@ components: superFollowersCount: type: integer superFollowsApplicationStatus: - type: string # enum: NotStarted + type: string + enum: [NotStarted] user_id: type: string pattern: "^[0-9]+$" diff --git a/src/openapi/schemas/content.yaml b/src/openapi/schemas/content.yaml index 1d91d6d..aa2e511 100644 --- a/src/openapi/schemas/content.yaml +++ b/src/openapi/schemas/content.yaml @@ -40,8 +40,7 @@ components: itemContent: $ref: "#/components/schemas/ItemContentUnion" clientEventInfo: - type: object - additionalProperties: true # todo + $ref: "#/components/schemas/ClientEventInfo" feedbackInfo: type: object additionalProperties: true # todo @@ -60,7 +59,8 @@ components: entryType: $ref: "#/components/schemas/ContentEntryType" # TimelineTimelineCursor displayType: - type: string # enum + type: string + enum: [Vertical, VerticalConversation] items: type: array items: @@ -112,8 +112,7 @@ components: - "itemContent" properties: clientEventInfo: - type: object - additionalProperties: true # todo + $ref: "#/components/schemas/ClientEventInfo" itemContent: $ref: "#/components/schemas/ItemContentUnion" @@ -183,13 +182,13 @@ components: SocialContext: $ref: "#/components/schemas/SocialContext" userDisplayType: - type: string # enum + type: string + enum: [User, UserDetailed, SubscribableUser] user_results: $ref: "./user.yaml#/components/schemas/UserResults" ItemResult: required: - - "result" properties: __typename: $ref: "./typename.yaml#/components/schemas/TypeName" # null | TimelineTweet @@ -217,3 +216,17 @@ components: __typename: $ref: "./typename.yaml#/components/schemas/TypeName" # TimelineMessagePrompt additionalProperties: true # todo + + ClientEventInfo: + properties: + component: + # enum half_cover + type: string + element: + type: string + # august-2023-privacy-prompt-candidate + # pattern: "(([a-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]+$" + details: + type: object + additionalProperties: true # todo diff --git a/src/openapi/schemas/instruction.yaml b/src/openapi/schemas/instruction.yaml index 881f9dd..25a3def 100644 --- a/src/openapi/schemas/instruction.yaml +++ b/src/openapi/schemas/instruction.yaml @@ -15,6 +15,8 @@ components: - $ref: "#/components/schemas/TimelineReplaceEntry" - $ref: "#/components/schemas/TimelineShowAlert" - $ref: "#/components/schemas/TimelineTerminateTimeline" + - $ref: "#/components/schemas/TimelineShowCover" + discriminator: propertyName: type mapping": # deprecated @@ -25,6 +27,7 @@ components: TimelineReplaceEntry: "#/components/schemas/TimelineReplaceEntry" TimelineShowAlert: "#/components/schemas/TimelineShowAlert" TimelineTerminateTimeline: "#/components/schemas/TimelineTerminateTimeline" + TimelineShowCover: "#/components/schemas/TimelineShowCover" InstructionType: type: string @@ -37,6 +40,7 @@ components: TimelineReplaceEntry, TimelineShowAlert, TimelineTerminateTimeline, + TimelineShowCover, ] TimelineAddEntries: @@ -162,3 +166,133 @@ components: sortIndex: type: string pattern: "[0-9]+$" + + TimelineShowCover: + required: + - type + - clientEventInfo + - cover + properties: + type: + $ref: "#/components/schemas/InstructionType" # TimelineShowCover + clientEventInfo: + $ref: "./content.yaml#/components/schemas/ClientEventInfo" + cover: + $ref: "#/components/schemas/TimelineHalfCover" + + TimelineHalfCover: + required: + - type + - halfCoverDisplayType + - primaryText + - primaryCoverCta + - secondaryText + - impressionCallbacks + - dismissible + properties: + type: + type: string + enum: [TimelineHalfCover] + halfCoverDisplayType: + type: string + enum: [Cover] + primaryText: + $ref: "#/components/schemas/Text" + secondaryText: + $ref: "#/components/schemas/Text" + primaryCoverCta: + $ref: "#/components/schemas/CoverCta" + impressionCallbacks: + type: array + items: + $ref: "#/components/schemas/Callback" + dismissible: + type: boolean + + Text: + required: + - text + - entities + properties: + text: + type: string + entities: + type: array + items: + $ref: "#/components/schemas/TextEntity" + + TextEntity: + required: + - fromIndex + - toIndex + - ref + properties: + fromIndex: + type: integer + toIndex: + type: integer + ref: + $ref: "#/components/schemas/TextEntityRef" + + TextEntityRef: + required: + - type + - url + - urlType + properties: + type: + type: string + enum: [TimelineUrl] + url: + type: string + format: uri + urlType: + type: string + enum: [ExternalUrl] + + CoverCta: + required: + - text + - ctaBehavior + - callbacks + - clientEventInfo + - buttonStyle + properties: + Text: + type: string + ctaBehavior: + $ref: "#/components/schemas/TimelineCoverBehavior" + callbacks: + type: array + items: + $ref: "#/components/schemas/Callback" + clientEventInfo: + $ref: "#/components/schemas/CtaClientEventInfo" + buttonStyle: + type: string + enum: ["Primary"] + + TimelineCoverBehavior: + required: + - type + properties: + type: + type: string + enum: [TimelineCoverBehaviorDismiss] + + Callback: + required: + - endpoint + properties: + endpoint: + type: string + format: uri + # pattern: '^/1\.1/[a-z]+/[a-z]+\.json\?[a-z_]+=[a-z0-9-]+(&[a-z_]+=[a-z0-9-]+)+?$' #/1.1/onboarding/fatigue.json?{params} + + CtaClientEventInfo: + required: + - action + properties: + action: + type: string + enum: [primary_cta] diff --git a/src/openapi/schemas/tweet.yaml b/src/openapi/schemas/tweet.yaml index baf63f8..5ae992f 100644 --- a/src/openapi/schemas/tweet.yaml +++ b/src/openapi/schemas/tweet.yaml @@ -38,11 +38,12 @@ components: Tweet: required: - "rest_id" - - "core" + # If the tweet has been edited, this property does not exist. + # - "core" - "edit_control" - # - "edit_prespective" - "is_translatable" - - "legacy" + # - "source" + # - "legacy" - "views" properties: @@ -54,86 +55,130 @@ components: core: $ref: "./user.yaml#/components/schemas/UserResultCore" card: - type: object - properties: - rest_id: - type: string - legacy: - type: object - required: - - "binding_values" - - "name" - - "url" - properties: - name: - type: string - url: - type: string - binding_values: - type: array - items: - type: object - required: - - "key" - - "value" - properties: - key: - type: string - value: - type: object - required: - - "type" - properties: - string_value: - type: string - boolean_value: - type: boolean - scribe_key: - type: string - type: - type: string + $ref: "#/components/schemas/TweetCard" unmention_data: type: object additionalProperties: true # todo edit_control: - type: object - properties: - edit_tweet_ids: - type: array - items: - type: string - pattern: "^[0-9]+$" - editable_until_msecs: - type: string - pattern: "^[0-9]+$" - is_edit_eligible: - type: boolean - edits_remaining: - type: string - pattern: "^[0-9]+$" + $ref: "#/components/schemas/TweetEditControl" edit_prespective: - type: object - properties: - favorited: - type: boolean - retweeted: - type: boolean + $ref: "#/components/schemas/TweetEditPrespective" is_translatable: type: boolean default: false + source: + type: string # html (Twitter for Android) legacy: $ref: "#/components/schemas/TweetLegacy" views: - type: object - properties: - count: - type: string - pattern: "^[0-9]+$" - state: - type: string # enum + $ref: "#/components/schemas/TweetView" quoted_status_result: $ref: "./content.yaml#/components/schemas/ItemResult" + TweetEditControl: + properties: + edit_tweet_ids: + type: array + items: + type: string + pattern: "^[0-9]+$" + editable_until_msecs: + type: string + pattern: "^[0-9]+$" + is_edit_eligible: + type: boolean + edits_remaining: + type: string + pattern: "^[0-9]+$" + initial_tweet_id: + type: string + pattern: "^[0-9]+$" + edit_control_initial: + $ref: "#/components/schemas/TweetEditControlInitial" + + TweetEditControlInitial: + required: + - "edit_tweet_ids" + - "editable_until_msecs" + - "is_edit_eligible" + - "edits_remaining" + properties: + edit_tweet_ids: + type: array + items: + type: string + pattern: "^[0-9]+$" + editable_until_msecs: + type: string + pattern: "^[0-9]+$" + is_edit_eligible: + type: boolean + edits_remaining: + type: string + pattern: "^[0-9]+$" + + TweetEditPrespective: + properties: + favorited: + type: boolean + retweeted: + type: boolean + TweetView: + required: + - "state" + properties: + count: + type: string + pattern: "^[0-9]+$" + state: + type: string + enum: [EnabledWithCount] + + TweetCard: + properties: + rest_id: + type: string + legacy: + $ref: "#/components/schemas/TweetCardLegacy" + + TweetCardLegacy: + required: + - "binding_values" + - "name" + - "url" + properties: + name: + type: string + url: + type: string + binding_values: + type: array + items: + $ref: "#/components/schemas/TweetCardLegacyBindingValue" + + TweetCardLegacyBindingValue: + required: + - "key" + - "value" + properties: + key: + type: string + value: + $ref: "#/components/schemas/TweetCardLegacyBindingValueData" + + TweetCardLegacyBindingValueData: + required: + - "type" + properties: + string_value: + type: string + boolean_value: + type: boolean + scribe_key: + type: string + type: + type: string + TweetLegacy: required: - "bookmark_count" @@ -202,23 +247,24 @@ components: type: string pattern: "^[0-9]+$" self_thread: - type: object - required: - - "id_str" - properties: - id_str: - type: string - pattern: "^[0-9]+$" + $ref: "#/components/schemas/SelfThread" extended_entities: $ref: "#/components/schemas/ExtendedEntities" + SelfThread: + required: + - "id_str" + properties: + id_str: + type: string + pattern: "^[0-9]+$" + Entities: required: - "hashtags" - "symbols" - "user_mentions" - "urls" - # - "media" properties: hashtags: type: array @@ -271,27 +317,25 @@ components: items: type: integer - ExtendedEntities: - required: - - "media" - properties: - media: - type: array - items: - $ref: "#/components/schemas/Media" - Media: required: + - "display_url" + - "expanded_url" - "id_str" - "indices" - "media_url_https" - - "url" - - "display_url" - - "expanded_url" - "type" + - "url" + # - "features" - "sizes" - "original_info" properties: + display_url: + type: string + format: uri + expanded_url: + type: string + format: uri id_str: type: string pattern: "^[0-9]+$" @@ -302,35 +346,190 @@ components: media_url_https: type: string format: uri + type: + type: string + enum: [photo, video, animated_gif] url: type: string format: uri + features: + type: object + sizes: + $ref: "#/components/schemas/MediaSizes" + original_info: + $ref: "#/components/schemas/MediaOriginalInfo" + + ExtendedEntities: + required: + - "media" + properties: + media: + type: array + items: + $ref: "#/components/schemas/MediaExtended" + + MediaExtended: + required: + - "display_url" + - "expanded_url" + - "id_str" + - "indices" + - "media_key" + - "media_url_https" + - "type" + - "url" + - "ext_media_availability" + # - "features" + - "sizes" + - "original_info" + properties: display_url: type: string format: uri expanded_url: type: string format: uri - type: - type: string # enum - sizes: - type: object - additionalProperties: true # todo - original_info: - type: object - properties: - height: - type: integer - width: - type: integer - focus_rects: - type: array - items: - type: object - additionalProperties: true # todo + id_str: + type: string + pattern: "^[0-9]+$" + indices: + type: array + items: + type: integer media_key: type: string - pattern: "^[0-9]+_[0-9]+$" + media_url_https: + type: string + format: uri + type: + type: string + enum: [photo, video, animated_gif] + url: + type: string + format: uri + additional_media_info: + $ref: "#/components/schemas/AdditionalMediaInfo" + mediaStats: + $ref: "#/components/schemas/mediaStats" ext_media_availability: + $ref: "#/components/schemas/extMediaAvailability" + features: type: object - additionalProperties: true # todo + sizes: + $ref: "#/components/schemas/MediaSizes" + original_info: + $ref: "#/components/schemas/MediaOriginalInfo" + video_info: + $ref: "#/components/schemas/MediaVideoInfo" + + MediaOriginalInfo: + required: + - "height" + - "width" + properties: + height: + type: integer + width: + type: integer + focus_rects: + type: array + items: + $ref: "#/components/schemas/MediaOriginalInfoFocusRect" + + MediaOriginalInfoFocusRect: + type: object + required: + - "x" + - "y" + - "w" + - "h" + properties: + x: + type: integer + y: + type: integer + w: + type: integer + h: + type: integer + + MediaVideoInfo: + required: + - "aspect_ratio" + - "variants" + properties: + aspect_ratio: + type: array + items: + type: integer + duration_millis: + type: integer + variants: + type: array + items: + $ref: "#/components/schemas/MediaVideoInfoVariant" + + MediaVideoInfoVariant: + required: + - "content_type" + - "url" + properties: + bitrate: + type: integer + content_type: + type: string + url: + type: string + format: uri + + AdditionalMediaInfo: + required: + - "monetizable" + properties: + monetizable: + type: boolean + + mediaStats: + required: + - "viewCount" + properties: + viewCount: + type: integer + + extMediaAvailability: + # required: + # - "status" + properties: + status: + type: string + enum: [Available] + + MediaSizes: + required: + - "large" + - "medium" + - "small" + - "thumb" + properties: + large: + $ref: "#/components/schemas/MediaSize" + medium: + $ref: "#/components/schemas/MediaSize" + small: + $ref: "#/components/schemas/MediaSize" + thumb: + $ref: "#/components/schemas/MediaSize" + + MediaSize: + required: + - "w" + - "h" + - "resize" + properties: + w: + type: integer + h: + type: integer + resize: + type: string + enum: [crop, fit] diff --git a/src/openapi/schemas/typename.yaml b/src/openapi/schemas/typename.yaml index 7be38b1..85fd323 100644 --- a/src/openapi/schemas/typename.yaml +++ b/src/openapi/schemas/typename.yaml @@ -18,6 +18,7 @@ components: TweetTombstone, TimelinePrompt, TimelineMessagePrompt, + TweetUnavailable, Tweet, User, UserUnavailable, diff --git a/test/python/replace.py b/test/python/replace.py new file mode 100644 index 0000000..ab9b5f9 --- /dev/null +++ b/test/python/replace.py @@ -0,0 +1,23 @@ +import glob + +for file in glob.glob("python_generated/openapi_client/models/*.py"): + with open(file, "r") as f: + text = f.read() + + indent = " " + + text = text.replace( + f"{indent}{indent}try:", + f"{indent}{indent}if match == 0:", + ) + text = text.replace( + f"{indent}{indent}except (ValidationError, ValueError) as e:", + f"{indent}{indent}else:", + ) + text = text.replace( + f"{indent}{indent}{indent}error_messages.append(str(e))", + f"{indent}{indent}{indent}pass", + ) + + with open(file, "w") as f: + f.write(text) diff --git a/test/python/test_serialize.py b/test/python/test_serialize.py index 5e9668d..4968856 100644 --- a/test/python/test_serialize.py +++ b/test/python/test_serialize.py @@ -5,6 +5,10 @@ import base64 import openapi_client as pt from pathlib import Path import time +import glob +import aenum +import concurrent.futures +import traceback logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)s %(message)s") @@ -14,23 +18,7 @@ TWITTER_SESSION = os.environ.get("TWITTER_SESSION", None) ERROR_UNCATCHED = os.environ.get("ERROR_UNCATCHED", "false").lower() == "true" SLEEP_TIME = float(os.environ.get("SLEEP", "0")) CUESOR_TEST_COUNT = int(os.environ.get("CUESOR_TEST_COUNT", "3")) - - -if Path("cookie.json").exists(): - with open("cookie.json", "r") as f: - cookies = json.load(f) -elif TWITTER_SESSION is not None: - data = base64.b64decode(TWITTER_SESSION).decode("utf-8") - cookies = json.loads(data) -else: - commands = ["python -m pip install tweepy_authlib", "python tools/login.py"] - raise Exception(f'cookie.json not found. Please run `{"; ".join(commands)}` first.') - -cookies_str = "; ".join([f"{k}={v}" for k, v in cookies.items()]) - - -with open("src/config/placeholder.json", "r") as f: - placeholder = json.load(f) +STRICT_MODE = os.environ.get("STRICT_MODE", "false").lower() == "true" def get_key(snake_str): @@ -74,10 +62,68 @@ def get_kwargs(key, additional): return kwargs +def match_rate_zero(key): + if STRICT_MODE: + raise Exception(f"Strict mode: {key}") + return 0 + + +def match_rate(a, b, key=""): + if isinstance(a, aenum.Enum): + a = a.value + if isinstance(b, aenum.Enum): + b = b.value + if a is None and b is False: + return 1 + if a is False and b is None: + return 1 + if type(a) != type(b): + return match_rate_zero(key) + if type(a) == dict and type(b) == dict: + if len(a) == 0 and len(b) == 0: + return 1 + if len(a) == 0 or len(b) == 0: + return match_rate_zero(key) + + data = [match_rate(a.get(k), b.get(k), key=f"{key}.{k}") for k in a.keys()] + + return sum(data) / len(a) + if type(a) == list and type(b) == list: + if len(a) == 0 and len(b) == 0: + return 1 + if len(a) != len(b): + return match_rate_zero(a, b, key=key) + data = [match_rate(a[i], b[i], key=f"{key}[{i}]") for i in range(len(a))] + return sum(data) / len(a) + if a == b: + return 1 + return match_rate_zero(a, b, key=key) + + +def save_cache(data): + rand = time.time_ns() + os.makedirs("cache", exist_ok=True) + with open(f"cache/{rand}.json", "w+") as f: + json.dump(data, f, indent=4) + + +def task_callback(file, thread=True): + try: + with open(file, "r") as f: + cache = json.load(f) + data = pt.__dict__[cache["type"]].from_json(cache["raw"]) + rate = match_rate(data.to_dict(), json.loads(cache["raw"])) + return rate, file + except Exception as e: + if thread: + return 0, file + else: + raise + + def error_dump(e): if ERROR_UNCATCHED: raise - import traceback logger.error("==========[STACK TRACE]==========") for trace in traceback.format_exc().split("\n"): @@ -85,76 +131,130 @@ def error_dump(e): logger.info("================================") -api_conf = pt.Configuration( - api_key={ - "ClientLanguage": "en", - "ActiveUser": "yes", - "AuthType": "OAuth2Session", - "CsrfToken": cookies["ct0"], - }, -) +if __name__ == "__main__": + if Path("cookie.json").exists(): + with open("cookie.json", "r") as f: + cookies = json.load(f) + elif TWITTER_SESSION is not None: + data = base64.b64decode(TWITTER_SESSION).decode("utf-8") + cookies = json.loads(data) + else: + commands = ["python -m pip install tweepy_authlib", "python tools/login.py"] + raise Exception( + f'cookie.json not found. Please run `{"; ".join(commands)}` first.' + ) -api_conf.access_token = "AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA" + cookies_str = "; ".join([f"{k}={v}" for k, v in cookies.items()]) -api_client = pt.ApiClient(configuration=api_conf, cookie=cookies_str) -api_client.user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36" + with open("src/config/placeholder.json", "r") as f: + placeholder = json.load(f) -error_count = 0 + fail = [] + with concurrent.futures.ProcessPoolExecutor() as executor: + tasks = [executor.submit(task_callback, x) for x in glob.glob("cache/*.json")] + for task in concurrent.futures.as_completed(tasks): + rate, file = task.result() + if rate < 1: + fail.append(file) + logger.info(f"Match rate: {rate}") -for x in [pt.DefaultApi, pt.TweetApi, pt.UserApi, pt.UsersApi, pt.UserListApi]: - for props, fn in x.__dict__.items(): - if not callable(fn): - continue - if props.startswith("__") or props.endswith("_with_http_info"): - continue + logger.info(f"Fail: {len(fail)} / {len(glob.glob('cache/*.json'))}") - key = get_key(props) - cursor_list = set([None]) - cursor_history = set() + for file in fail: + task_callback(file, thread=False) + logger.info(f"Match rate: {rate}") - try: - for _ in range(CUESOR_TEST_COUNT): - cursor = cursor_list.pop() - cursor_history.add(cursor) - logger.info(f"Try: {key} {cursor}") + api_conf = pt.Configuration( + api_key={ + "ClientLanguage": "en", + "ActiveUser": "yes", + "AuthType": "OAuth2Session", + "CsrfToken": cookies["ct0"], + }, + ) - kwargs = get_kwargs(key, {} if cursor is None else {"cursor": cursor}) - res: dict = getattr(x(api_client), props)(**kwargs).to_dict() + api_conf.access_token = "AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA" - new_cursor = set(get_cursor(res, find_cursor)) - cursor_history - cursor_list.update(new_cursor) - # logger.info(f"Find cursor: {len(new_cursor)}") + api_client = pt.ApiClient(configuration=api_conf, cookie=cookies_str) + api_client.user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36" - if res.get("errors") is not None: - logger.error(res) - error_count += 1 + error_count = 0 - if len(cursor_list) == 0: - break - time.sleep(SLEEP_TIME) + for x in [pt.DefaultApi, pt.TweetApi, pt.UserApi, pt.UsersApi, pt.UserListApi]: + for props, fn in x.__dict__.items(): + if not callable(fn): + continue + if props.startswith("__") or not props.endswith("_with_http_info"): + continue - except Exception as e: - error_dump(e) - error_count += 1 + key = get_key(props[:-15]) + cursor_list = set([None]) + cursor_history = set() + try: + for _ in range(CUESOR_TEST_COUNT): + cursor = cursor_list.pop() + cursor_history.add(cursor) + logger.info(f"Try: {key} {cursor}") -try: - logger.info(f"Try: Self UserByScreenName Test") - kwargs = get_kwargs("UserByScreenName", {"screen_name": "a810810931931"}) - res = pt.UserApi(api_client).get_user_by_screen_name(**kwargs).to_dict() - if not res["data"]["user"]["result"]["legacy"]["screen_name"] == "a810810931931": - raise Exception("UserByScreenName failed") -except Exception as e: - error_dump(e) - error_count += 1 + kwargs = get_kwargs( + key, {} if cursor is None else {"cursor": cursor} + ) + res: pt.ApiResponse = getattr(x(api_client), props)(**kwargs) + data = res.data.to_dict() -try: - logger.info(f"Try: Self UserTweets Test") - kwargs = get_kwargs("UserTweets", {"userId": "1180389371481976833"}) - pt.TweetApi(api_client).get_user_tweets(**kwargs) -except Exception as e: - error_dump(e) - error_count += 1 + save_cache( + { + "raw": res.raw_data, + "type": res.data.__class__.__name__, + } + ) -if error_count > 0: - exit(1) + new_cursor = set(get_cursor(data, find_cursor)) - cursor_history + cursor_list.update(new_cursor) + + rate = match_rate(data, json.loads(res.raw_data)) + logger.info(f"Match rate: {rate}") + + if data.get("errors") is not None: + logger.error(data) + error_count += 1 + + if len(cursor_list) == 0: + break + time.sleep(SLEEP_TIME) + + except Exception as e: + error_dump(e) + error_count += 1 + + try: + logger.info(f"Try: Self UserByScreenName Test") + kwargs = get_kwargs("UserByScreenName", {"screen_name": "a810810931931"}) + res = pt.UserApi(api_client).get_user_by_screen_name_with_http_info(**kwargs) + data = res.data.to_dict() + + rate = match_rate(data, json.loads(res.raw_data)) + logger.info(f"Match rate: {rate}") + screen_name = data["data"]["user"]["result"]["legacy"]["screen_name"] + if not screen_name == "a810810931931": + raise Exception("UserByScreenName failed") + except Exception as e: + error_dump(e) + error_count += 1 + + try: + logger.info(f"Try: Self UserTweets Test") + kwargs = get_kwargs("UserTweets", {"userId": "1180389371481976833"}) + res = pt.TweetApi(api_client).get_user_tweets_with_http_info(**kwargs) + data = res.data.to_dict() + + rate = match_rate(data, json.loads(res.raw_data)) + logger.info(f"Match rate: {rate}") + + except Exception as e: + error_dump(e) + error_count += 1 + + if error_count > 0: + exit(1)