paint-brush
Twitter API-ä düşünmek, şonuň üçin özüňiz düzüp bilersiňiztarapyndan@trekhleb
194 oka Täze taryh

Twitter API-ä düşünmek, şonuň üçin özüňiz düzüp bilersiňiz

tarapyndan Oleksii Trekhleb22m2024/12/16
Read on Terminal Reader

Örän uzyn; Okamak

Bu makalada, X (Twitter) öý tertibi (x.com/home) API-iň nähili düzülendigini we köp kynçylyklary çözmek üçin haýsy çemeleşmeleri ulanýandygyny öwrenýäris.
featured image - Twitter API-ä düşünmek, şonuň üçin özüňiz düzüp bilersiňiz
Oleksii Trekhleb HackerNoon profile picture
0-item
1-item

Ulgamyň API dizaýnyna gezek gelende, programma in engineenerleri köplenç dürli görnüşleri göz öňünde tutýarlar REST vs RPC vs GraphQL (ýa-da beýleki gibrid çemeleşmeler) belli bir meselä ýa-da taslama üçin iň laýykdygyny kesgitlemek üçin.


Bu makalada X ( Twitter ) öý tertibi (x.com/home) API-iň nähili düzülendigini we aşakdaky kynçylyklary çözmek üçin haýsy çemeleşmeleri ulanýandygyny öwrenýäris:

  • Tweetleriň sanawyny nädip almaly

  • Saýlamagy we sahypany nädip etmeli

  • Iýerarhiki / baglanyşdyrylan guramalary (twitler, ulanyjylar, metbugat) nädip yzyna gaýtarmaly?

  • Tweet jikme-jikliklerini nädip almaly

  • Twitteri nädip "halamaly"


Bu kynçylyklary diňe API derejesinde öwreneris, arkanyň ýerine ýetirilişine gara guty hökmünde serederis, sebäbi arka koduň özi ýok.


Bu ýerde takyk haýyşlary we jogaplary görkezmek kyn we kyn bolup biler, sebäbi çuňňur höwürtgeleýän we gaýtalanýan zatlary okamak kyn. Talap / jogap ýükleme gurluşyny görmegi aňsatlaşdyrmak üçin, TypeScript-de öý tertibi API-ni "ýazmaga" synanyşdym. Şonuň üçin haýyş / jogap mysallary barada aýdylanda, hakyky JSON obýektleriniň ýerine haýyş we jogap görnüşlerini ulanaryn. Şeýle hem, görnüşleriň ýönekeýleşdirilendigini we gysga wagtlyk köp häsiýetleriň ýokdugyny ýatdan çykarmaň.


Inhli görnüşleri tapyp bilersiňiz görnüşleri / x.ts faýl ýa-da şu makalanyň aşagyndaky "Goşundy: typeshli görnüşler bir ýerde" bölüminde.

Tweetleriň sanawyny almak

Ahyrky nokat we haýyş / jogap gurluşy

Öý tertibi üçin twitleriň sanawyny almak POST haýyşy bilen aşakdaky ahyrky nokada başlaýar:


 POST https://x.com/i/api/graphql/{query-id}/HomeTimeline


Ine, ýönekeý haýyş görnüşi görnüşi:


 type TimelineRequest = { queryId: string; // 's6ERr1UxkxxBx4YundNsXw' variables: { count: number; // 20 cursor?: string; // 'DAAACgGBGedb3Vx__9sKAAIZ5g4QENc99AcAAwAAIAIAAA' seenTweetIds: string[]; // ['1867041249938530657', '1867041249938530659'] }; features: Features; }; type Features = { articles_preview_enabled: boolean; view_counts_everywhere_api_enabled: boolean; // ... }


Ine, ýönekeý jogap görnüşiniň görnüşi (aşakdaky jogap görnüşlerine has çuňňur öwreneris):


 type TimelineResponse = { data: { home: { home_timeline_urt: { instructions: (TimelineAddEntries | TimelineTerminateTimeline)[]; responseObjects: { feedbackActions: TimelineAction[]; }; }; }; }; }; type TimelineAddEntries = { type: 'TimelineAddEntries'; entries: (TimelineItem | TimelineCursor | TimelineModule)[]; }; type TimelineItem = { entryId: string; // 'tweet-1867041249938530657' sortIndex: string; // '1866561576636152411' content: { __typename: 'TimelineTimelineItem'; itemContent: TimelineTweet; feedbackInfo: { feedbackKeys: ActionKey[]; // ['-1378668161'] }; }; }; type TimelineTweet = { __typename: 'TimelineTweet'; tweet_results: { result: Tweet; }; }; type TimelineCursor = { entryId: string; // 'cursor-top-1867041249938530657' sortIndex: string; // '1866961576813152212' content: { __typename: 'TimelineTimelineCursor'; value: string; // 'DACBCgABGedb4VyaJwuKbIIZ40cX3dYwGgaAAwAEAEEAA' cursorType: 'Top' | 'Bottom'; }; }; type ActionKey = string;


Maglumatlary "almak" REST meňzeş API üçin adaty däl, ýöne GraphQL meňzeş API üçin adaty "POSTing" arkaly amala aşyrylýandygyny bellemek gyzykly. Şeýle hem, URL-iň graphql bölegi X-nyň API üçin GraphQL tagamyny ulanýandygyny görkezýär.


Bu ýerde "tagam" sözüni ulanýaryn, sebäbi haýyş bedeniniň özi arassa däl GraphQL talap almak isleýän ähli häsiýetlerimizi sanap, zerur jogap gurluşyny suratlandyryp bileris:


 # An example of a pure GraphQL request structure that is *not* being used in the X API. { tweets { id description created_at medias { kind url # ... } author { id name # ... } # ... } }


Bu ýerdäki çaklama, öý tertibi API arassa GraphQL API däl-de, eýsem birnäçe çemeleşmäniň garyndysydyr . Parametrleri şuňa meňzeş POST haýyşynda geçirmek "işleýän" RPC çagyryşyna has ýakyn görünýär. Theöne şol bir wagtyň özünde, GraphQL aýratynlyklaryny HomeTimeline ahyrky nokady dolandyryjy / kontrolleýjiniň arkasyndaky bir ýerde ulanyp boljak ýaly. Munuň ýaly garyşyk miras kody ýa-da haýsydyr bir dowam edýän göçürme sebäpli hem bolup biler. Againöne ýene-de meniň çaklamalarym.


Şol bir wagtyň özünde, şol bir TimelineRequest.queryId iň API URL-de we API haýyş organynda ulanylýandygyny görüp bilersiňiz. Bu talapId, ähtimal arka tarapda döredilen bolsa, soňra main.js bukjasyna ornaşdyrylýar, soňra bolsa arkadan maglumatlary alanda ulanylýar. Bu queryId nähili ulanylýandygyna düşünmek maňa kyn, sebäbi X-nyň arkasy biziň üçin gara guty. Againöne, ýene-de bir gezek, bu ýerde birneme öndürijilik optimizasiýasy (öňünden hasaplanan talaplaryň netijelerini gaýtadan ulanmak?), Keşlemek (Apollon bilen baglanyşyklymy?), Düzediş (queryId tarapyndan surnallara goşulmak üçin) zerur bolup biler. ýa-da yzarlamak / gözlemek maksatlary.

Şeýle hem, TimelineResponse -da twitleriň sanawy däl-de, eýsem “wagt tertibine twit goşmak” (“ TimelineAddEntries görnüşine serediň) ýa-da “wagt TimelineTerminateTimeline ýatyrmak” ýaly görkezmeleriň sanawy bar. görnüşi).


TimelineAddEntries görkezmesiniň özi dürli görnüşli guramalary hem öz içine alyp biler:

  • Twitter - TimelineItem görnüşine serediň
  • Kursorlar - TimelineCursor görnüşine serediň
  • Söhbetler / teswirler / sapaklar - TimelineModule görnüşine serediň


 type TimelineResponse = { data: { home: { home_timeline_urt: { instructions: (TimelineAddEntries | TimelineTerminateTimeline)[]; // <-- Here // ... }; }; }; }; type TimelineAddEntries = { type: 'TimelineAddEntries'; entries: (TimelineItem | TimelineCursor | TimelineModule)[]; // <-- Here };


Bu giňeliş nukdaýnazaryndan gyzykly, sebäbi öý tertibinde API-ni kän bir üýtgetmän görkezilip bilinjek zatlaryň has giň görnüşine mümkinçilik berýär.

Sahypa

TimelineRequest.variables.count emläk birbada näçe twit almak isleýändigimizi kesgitleýär (her sahypa üçin). Dymmaklyk 20. Şeýle-de bolsa, TimelineAddEntries.entries massiwinde 20-den gowrak twit yzyna gaýtarylyp bilner. Mysal üçin, massiwde birinji sahypa ýüklemek üçin 37 ýazgy bolup biler, sebäbi ol twitleri (29), berkidilen twitleri (1), mahabatlandyrylan twitleri (5) we sahypa kursorlaryny (2) öz içine alýar. Näme üçin talap edilýän 20 sany 29 sany yzygiderli twitiň bardygyny bilemok.


TimelineRequest.variables.cursor kursor esasly sahypa üçin jogapkärdir.


" Kursor sahypasy köplenç hakyky ýazgylar üçin ulanylýar, ýygylyk sebäpli täze ýazgylar goşulýar we maglumatlary okanyňyzda köplenç iň soňky netijeleri görýärsiňiz. Bu zatlary taşlamak we şol bir elementi bir gezekden köp görkezmek mümkinçiligini aradan aýyrýar. kursor esasly sahypa, maglumatlardaky indiki elementleriň nireden alynmalydygyny yzarlamak üçin hemişelik görkeziji (ýa-da kursor) ulanylýar. " Serediň Ofset sahypasy vs Kursor sahypasy kontekst üçin sapak.


Ilkinji gezek TimelineRequest.variables.cursor twitleriň sanawyny alanyňyzda, ýokarky twitleri şahsylaşdyrylan twitleriň deslapky (ähtimal öňünden hasaplanan) sanawyndan almak isleýäris.


Muňa garamazdan, jogapda twit maglumatlary bilen birlikde arka tarapy kursor ýazgylaryny hem yzyna berýär. Ine, jogap görnüşiniň iýerarhiýasy: TimelineResponse → TimelineAddEntries → TimelineCursor :


 type TimelineResponse = { data: { home: { home_timeline_urt: { instructions: (TimelineAddEntries | TimelineTerminateTimeline)[]; // <-- Here // ... }; }; }; }; type TimelineAddEntries = { type: 'TimelineAddEntries'; entries: (TimelineItem | TimelineCursor | TimelineModule)[]; // <-- Here (tweets + cursors) }; type TimelineCursor = { entryId: string; sortIndex: string; content: { __typename: 'TimelineTimelineCursor'; value: string; // 'DACBCgABGedb4VyaJwuKbIIZ40cX3dYwGgaAAwAEAEEAA' <-- Here cursorType: 'Top' | 'Bottom'; }; };


Her sahypada "ýokarky" we "aşaky" kursorlar bilen birlikde twitleriň sanawy bar:


Sahypa maglumatlary ýüklenenden soň, häzirki sahypadan iki tarapa gidip, "aşaky" kursory ulanyp "öňki / köne" twitleri ýa-da "ýokarky" kursory ulanyp "indiki / täze" twitleri alyp bileris. Meniň pikirimçe, "ýokarky" kursory ulanyp, "indiki" twitleri almak iki ýagdaýda bolup geçýär: ulanyjy häzirki sahypany okaýarka ýa-da ulanyjy iýmini ýokaryk aýlap başlanda (we bar) keş görnüşli ýazgylar ýok ýa-da öňki ýazgylar ýerine ýetiriş sebäpleri bilen öçürilen bolsa).


X kursorynyň özi şeýle bolup biler: DAABCgABGemI6Mk__9sKAAIZ6MSYG9fQGwgAAwAAAAIAAA . Käbir API dizaýnlarynda kursor sanawdaky iň soňky ýazgynyň ID-sini ýa-da iň soňky görlen ýazgynyň belgisini öz içine alýan Base64 kodlanan setir bolup biler. Mysal üçin: eyJpZCI6ICIxMjM0NTY3ODkwIn0= --> {"id": "1234567890"} , soň bolsa bu maglumatlar maglumatlar bazasyny gözlemek üçin ulanylýar. X. Munuň .proto kodlaýyşdygyny ýa-da ýokdugyny bilemzok, we .proto habar kesgitlemesini hem bilmeýändigimiz üçin, arka tarapyň kursor setirine esaslanýan indiki twitleri nädip soramalydygyny bilip bileris.


TimelineResponse.variables.seenTweetIds parametri, müşderiniň häzirki işjeň sahypasyndan müşderiniň eýýäm gören haýsy twitleri barada serweri habar bermek üçin ulanylýar. Bu, serweriň netijeleriň indiki sahypalarynda iki gezek ýazylan twitleri öz içine almaýandygyna kömek eder.

Baglanan / iýerarhiki guramalar

Öý tertibi (ýa-da Öý iýmiti) ýaly API-lerde çözülmeli kynçylyklaryň biri, baglanyşdyrylan ýa-da iýerarhiki guramalary nädip yzyna gaýtarmalydygyny anyklamakdyr (ýagny tweet → user , tweet → media , media → author we ş.m.):


  • Ilki bilen diňe twitleriň sanawyny yzyna gaýtaryp, garaşly guramalary (ulanyjy jikme-jiklikleri ýaly) talap boýunça bir topar aýratyn soraglara getirmelimi?
  • Ora-da ilkinji ýüküň wagtyny we göwrümini köpeldip, ýöne soňraky ähli jaňlar üçin wagt tygşytlap, ähli maglumatlary birbada yzyna gaýtarmalymy?
    • Loadük göterijiligini azaltmak üçin bu ýagdaýda maglumatlary kadalaşdyrmalymy (ýagny şol bir ulanyjy köp twitleriň awtory we her bir twit guramasynda ulanyjy maglumatlaryny gaýtalap durmazlyk isleýäris)?
  • Ora-da ýokardaky çemeleşmeleriň utgaşmasy bolmalymy?


Geliň, X-yň nähili işleýändigini göreliň.

Mundan öň TimelineTweet görnüşinde Tweet kiçi görnüşi ulanylýar. Görnüşine seredeliň:


 export type TimelineResponse = { data: { home: { home_timeline_urt: { instructions: (TimelineAddEntries | TimelineTerminateTimeline)[]; // <-- Here // ... }; }; }; }; type TimelineAddEntries = { type: 'TimelineAddEntries'; entries: (TimelineItem | TimelineCursor | TimelineModule)[]; // <-- Here }; type TimelineItem = { entryId: string; sortIndex: string; content: { __typename: 'TimelineTimelineItem'; itemContent: TimelineTweet; // <-- Here // ... }; }; type TimelineTweet = { __typename: 'TimelineTweet'; tweet_results: { result: Tweet; // <-- Here }; }; // A Tweet entity type Tweet = { __typename: 'Tweet'; core: { user_results: { result: User; // <-- Here (a dependent User entity) }; }; legacy: { full_text: string; // ... entities: { // <-- Here (a dependent Media entities) media: Media[]; hashtags: Hashtag[]; urls: Url[]; user_mentions: UserMention[]; }; }; }; // A User entity type User = { __typename: 'User'; id: string; // 'VXNlcjoxNDUxM4ADSG44MTA4NDc4OTc2' // ... legacy: { location: string; // 'San Francisco' name: string; // 'John Doe' // ... }; }; // A Media entity type Media = { // ... source_user_id_str: string; // '1867041249938530657' <-- Here (the dependant user is being mentioned by its ID) url: string; // 'https://t.co/X78dBgtrsNU' features: { large: { faces: FaceGeometry[] }; medium: { faces: FaceGeometry[] }; small: { faces: FaceGeometry[] }; orig: { faces: FaceGeometry[] }; }; sizes: { large: MediaSize; medium: MediaSize; small: MediaSize; thumb: MediaSize; }; video_info: VideoInfo[]; };


Bu ýerde gyzykly zat, tweet → media we tweet → author ýaly garaşly maglumatlaryň köpüsiniň ilkinji jaňa jogap bermegi (indiki soraglar ýok).


Mundan başga-da, Tweet guramalary bilen User we Media baglanyşyklary kadalaşmaýar (iki twitiň awtory bir bolsa, maglumatlary her twit obýektinde gaýtalanar). Itöne oňat bolmaly ýaly, sebäbi belli bir ulanyjy üçin öý tertibiniň çäginde twitler köp ýazyjylar tarapyndan ýazylar we gaýtalanmak mümkin, ýöne seýrek.


Belli bir ulanyjynyň twitlerini almak üçin jogapkär UserTweets API” (bu ýerde açyp görkezmeýäris) başgaça pikir eder, ýöne görnüşi ýaly beýle däl. UserTweets şol bir ulanyjynyň twitleriniň sanawyny yzyna gaýtaryp berýär we her twit üçin şol bir ulanyjy maglumatlaryny gaýtalaýar. Bu gyzykly. Belki, çemeleşmäniň ýönekeýligi käbir maglumatlaryň ululygyny ýeňip biler (belki ulanyjy maglumatlary gaty kiçi hasaplanýar). Men anyk bilemok.


Edaralaryň gatnaşygy barada başga bir syn, Media guramasynyň User (awtor) bilen baglanyşygy hem bar. Itöne muny Tweet guramasynyň edişi ýaly gönüden-göni gurama arkaly däl-de, Media.source_user_id_str emläk arkaly baglanyşdyrýar.


Öý tertibindäki her "tweet" üçin "teswirler" (tebigaty boýunça "twitler" hem) asla alynmaýar. Tweet sapagyny görmek üçin ulanyjy jikme-jik görnüşini görmek üçin twite basmaly. Tweet sapagy TweetDetail ahyrky nokadyna jaň edip alynar (bu barada has aşakdaky "Tweet jikme-jiklik sahypasy" bölüminde).


Her Tweet bar bolan başga bir gurama FeedbackActions (ýagny “lessygy-ýygydan maslahat beriň” ýa-da “Az görüň”). FeedbackActions jogap obýektinde saklanyş usuly User we Media obýektleriniň saklanyş usulyndan tapawutlanýar. User we Media guramalary “ Tweet -iň bir bölegi bolsa-da, FeedbackActions TimelineItem.content.feedbackInfo.feedbackKeys massiwinde aýratyn saklanýar we ActionKey arkaly baglanyşdyrylýar. Bu meniň üçin birneme geň galdyrdy, sebäbi haýsydyr bir hereketiň gaýtadan ulanyp boljakdygy görünmeýär. Bir hereket diňe belli bir twit üçin ulanylýan ýaly. Şeýlelik bilen, FeedbackActions her twitde Media guramalary ýaly edilip bilner. Hereöne bu ýerde käbir gizlin çylşyrymlylygy ýitirip bilerin (her bir hereketiň çagalaryň hereketleri bolup biljekdigi ýaly).

Hereketler barada has giňişleýin maglumat aşakdaky "Tuweleme hereketleri" bölüminde.

Sortirlemek

Wagt görkezijisiniň tertipleşdiriş tertibi, sortIndex häsiýetleri arkaly arka tarapy bilen kesgitlenýär:


 type TimelineCursor = { entryId: string; sortIndex: string; // '1866961576813152212' <-- Here content: { __typename: 'TimelineTimelineCursor'; value: string; cursorType: 'Top' | 'Bottom'; }; }; type TimelineItem = { entryId: string; sortIndex: string; // '1866561576636152411' <-- Here content: { __typename: 'TimelineTimelineItem'; itemContent: TimelineTweet; feedbackInfo: { feedbackKeys: ActionKey[]; }; }; }; type TimelineModule = { entryId: string; sortIndex: string; // '73343543020642838441' <-- Here content: { __typename: 'TimelineTimelineModule'; items: { entryId: string, item: TimelineTweet, }[], displayType: 'VerticalConversation', }; };


sortIndex ” -iň özi '1867231621095096312' ýaly bir zat ýaly bolup biler. Göni gabat gelýär ýa-da a Gar ýagýan şahsyýet .


Aslynda jogapda görýän şahsyýetnamalaryňyzyň köpüsi (tweet şahsyýetnamalary) "Gar ýagýan ID" konwensiýasyna eýerýär we '1867231621095096312' ýaly görünýär.


Bu twitler ýaly guramalary tertiplemek üçin ulanylsa, ulgam Garfleýk ID-leriniň özboluşly hronologik görnüşini düzýär. Has ýokary sortIndex gymmaty bolan twitler ýa-da obýektler (has soňky wagt belligi) iýmitde has ýokary görünýär, pes bahalary bolanlar (köne wagt belligi) iýmitde has pes görünýär.


Ine, Garfleýk ID-ni (biziň ýagdaýymyzda sortIndex ) 1867231621095096312 ädimme-ädim kodlamak:

  • Wagt belgisini çykaryň:
    • Wagt belgisi “Garflake ID” -ni 22 bit (sag maglumat merkezi, işçi şahsyýetnamasy we yzygiderliligi üçin aşaky 22 biti aýyrmak üçin) bilen üýtgetmek arkaly alynýar: 1867231621095096312 → 445182709954
  • Twitter döwrüni goşuň:
    • Bu wagt belgisine Twitter-iň adaty döwrüni (1288834974657) goşmak, millisekuntda UNIX wagt belgisini berýär: 445182709954 + 1288834974657 → 1734017684611ms
  • Adam okalýan senä öwüriň:
    • UNIX wagt belgisini UTC senesine öwürmek: 1734017684611ms → 2024-12-12 15:34:44.611 (UTC)


Şeýlelik bilen, bu ýerde öý tertibindäki twitleriň hronologiki tertipde düzülendigini çaklap bileris.

Tuweleme hereketler

Her twitde "Hereketler" menýusy bar.


Her twit üçin hereketler, TimelineItem.content.feedbackInfo.feedbackKeys massiwinde arka tarapdan gelýär we twitler bilen ActionKey arkaly baglanyşdyrylýar:


 type TimelineResponse = { data: { home: { home_timeline_urt: { instructions: (TimelineAddEntries | TimelineTerminateTimeline)[]; responseObjects: { feedbackActions: TimelineAction[]; // <-- Here }; }; }; }; }; type TimelineItem = { entryId: string; sortIndex: string; content: { __typename: 'TimelineTimelineItem'; itemContent: TimelineTweet; feedbackInfo: { feedbackKeys: ActionKey[]; // ['-1378668161'] <-- Here }; }; }; type TimelineAction = { key: ActionKey; // '-609233128' value: { feedbackType: 'NotRelevant' | 'DontLike' | 'SeeFewer'; // ... prompt: string; // 'This post isn't relevant' | 'Not interested in this post' | ... confirmation: string; // 'Thanks. You'll see fewer posts like this.' childKeys: ActionKey[]; // ['1192182653', '-1427553257'], ie NotInterested -> SeeFewer feedbackUrl: string; // '/2/timeline/feedback.json?feedback_type=NotRelevant&action_metadata=SRwW6oXZadPHiOczBBaAwPanEwE%3D' hasUndoAction: boolean; icon: string; // 'Frown' }; };


Bu ýerde gyzykly zat, bu tekiz hereketleriň aslynda agaç (ýa-da grafikmi? Barlamadym), sebäbi her bir hereketde çaga hereketleri bolup biler ( TimelineAction.value.childKeys massiwine serediň). Munuň manysy bar, mysal üçin, ulanyjy "Halama" hereketine basandan soň, ulanyjynyň näme üçin etmeýändigini düşündirmegiň usuly hökmünde "Bu ýazgy degişli däl" hereketini görkezmek bolup biler. twit halamok

Jikme-jiklik sahypasy

Ulanyjy twit jikme-jiklik sahypasyny görmek islänsoň (ýagny teswirleriň / twitleriň mowzugyny görmek üçin), ulanyjy twite basýar we aşakdaky nokada GET haýyşy ýerine ýetirilýär:


 GET https://x.com/i/api/graphql/{query-id}/TweetDetail?variables={"focalTweetId":"1867231621095096312","referrer":"home","controller_data":"DACABBSQ","rankingMode":"Relevance","includePromotedContent":true,"withCommunity":true}&features={"articles_preview_enabled":true}


Bu ýerde twitleriň sanawynyň näme üçin POST jaňy arkaly alynýandygy bilen gyzyklandym, ýöne her twit jikme-jigi GET jaňy arkaly alynýar. Gabat gelmeýän ýaly. Esasanam, bu gezek query-id , features we beýlekiler ýaly şuňa meňzeş talap parametrleriniň haýyş organynda däl-de, URL-de geçirilendigini ýadyňyzdan çykarmaň. Jogap formaty hem meňzeş we sanaw jaňynyň görnüşlerini gaýtadan ulanýar. Munuň sebäbini bilemok. Againöne ýene-de bir gezek, bu ýerde käbir fon çylşyrymlylygyny ýitirip biljekdigime ynanýaryn.

Ine, ýönekeý jogap görnüşleriniň görnüşleri:


 type TweetDetailResponse = { data: { threaded_conversation_with_injections_v2: { instructions: (TimelineAddEntries | TimelineTerminateTimeline)[], }, }, } type TimelineAddEntries = { type: 'TimelineAddEntries'; entries: (TimelineItem | TimelineCursor | TimelineModule)[]; }; type TimelineTerminateTimeline = { type: 'TimelineTerminateTimeline', direction: 'Top', } type TimelineModule = { entryId: string; // 'conversationthread-58668734545929871193' sortIndex: string; // '1867231621095096312' content: { __typename: 'TimelineTimelineModule'; items: { entryId: string, // 'conversationthread-1866876425669871193-tweet-1866876038930951193' item: TimelineTweet, }[], // Comments to the tweets are also tweets displayType: 'VerticalConversation', }; };


Jogap sanawyň jogabyna gaty meňzeýär (görnüşlerinde), şonuň üçin bu ýerde uzak saklamarys.


Bir gyzykly nuans, her twitiň "teswirleri" (ýa-da gepleşikleri) aslynda beýleki twitlerdir ( TimelineModule görnüşine serediň). Şeýlelik bilen, twit sapagy, TimelineTweet ýazgylarynyň sanawyny görkezip, öý tertibi iýmitine gaty meňzeýär. Bu owadan görünýär. API dizaýnyna ähliumumy we gaýtadan ulanyp boljak çemeleşmäniň gowy mysaly.

Twitteri halamak

Ulanyjy twiti halasa, aşakdaky nokada POST haýyşy ýerine ýetirilýär:


 POST https://x.com/i/api/graphql/{query-id}/FavoriteTweet


Ine, haýyş beden görnüşleri:


 type FavoriteTweetRequest = { variables: { tweet_id: string; // '1867041249938530657' }; queryId: string; // 'lI07N61twFgted2EgXILM7A' };


Ine, jogap bedeniniň görnüşleri:


 type FavoriteTweetResponse = { data: { favorite_tweet: 'Done', } }


Göni görünýär we API dizaýnyna RPC ýaly çemeleşmä meňzeýär.


Netije

X-nyň API mysalyna seredip, öý tertibi API dizaýnynyň käbir esasy böleklerine degip geçdik. Bilşime görä ýolda käbir çaklamalary etdim. Käbir zatlary nädogry düşündiren bolmagym we käbir çylşyrymly nuanslary sypdyran bolmagym mümkin. Thatöne muny göz öňünde tutup, indiki API Dizaýn sessiýasynda ulanyp boljak bu ýokary derejeli syndan käbir peýdaly düşünje alarsyňyz diýip umyt edýärin.


Ilki bilen, “Facebook”, “Reddit”, “YouTube” we beýlekilerden käbir maglumatlary almak we söweş synagyndan geçen iň oňat tejribeleri we çözgütleri ýygnamak üçin şuňa meňzeş ýokary tehnologiýaly web sahypalaryna girmek meýilnamam bardy. Muny etmäge wagt tapjagymy bilemok. Görer. Itöne gyzykly maşk bolup biler.

Goşundy: typeshli görnüşler bir ýerde

Salgylanmak üçin, şu ýere gitmek üçin ähli görnüşleri goşýaryn. Şeýle hem, ähli görnüşleri tapyp bilersiňiz görnüşleri / x.ts faýl.


 /** * This file contains the simplified types for X's (Twitter's) home timeline API. * * These types are created for exploratory purposes, to see the current implementation * of the X's API, to see how they fetch Home Feed, how they do a pagination and sorting, * and how they pass the hierarchical entities (posts, media, user info, etc). * * Many properties and types are omitted for simplicity. */ // POST https://x.com/i/api/graphql/{query-id}/HomeTimeline export type TimelineRequest = { queryId: string; // 's6ERr1UxkxxBx4YundNsXw' variables: { count: number; // 20 cursor?: string; // 'DAAACgGBGedb3Vx__9sKAAIZ5g4QENc99AcAAwAAIAIAAA' seenTweetIds: string[]; // ['1867041249938530657', '1867041249938530658'] }; features: Features; }; // POST https://x.com/i/api/graphql/{query-id}/HomeTimeline export type TimelineResponse = { data: { home: { home_timeline_urt: { instructions: (TimelineAddEntries | TimelineTerminateTimeline)[]; responseObjects: { feedbackActions: TimelineAction[]; }; }; }; }; }; // POST https://x.com/i/api/graphql/{query-id}/FavoriteTweet export type FavoriteTweetRequest = { variables: { tweet_id: string; // '1867041249938530657' }; queryId: string; // 'lI07N6OtwFgted2EgXILM7A' }; // POST https://x.com/i/api/graphql/{query-id}/FavoriteTweet export type FavoriteTweetResponse = { data: { favorite_tweet: 'Done', } } // GET https://x.com/i/api/graphql/{query-id}/TweetDetail?variables={"focalTweetId":"1867041249938530657","referrer":"home","controller_data":"DACABBSQ","rankingMode":"Relevance","includePromotedContent":true,"withCommunity":true}&features={"articles_preview_enabled":true} export type TweetDetailResponse = { data: { threaded_conversation_with_injections_v2: { instructions: (TimelineAddEntries | TimelineTerminateTimeline)[], }, }, } type Features = { articles_preview_enabled: boolean; view_counts_everywhere_api_enabled: boolean; // ... } type TimelineAction = { key: ActionKey; // '-609233128' value: { feedbackType: 'NotRelevant' | 'DontLike' | 'SeeFewer'; // ... prompt: string; // 'This post isn't relevant' | 'Not interested in this post' | ... confirmation: string; // 'Thanks. You'll see fewer posts like this.' childKeys: ActionKey[]; // ['1192182653', '-1427553257'], ie NotInterested -> SeeFewer feedbackUrl: string; // '/2/timeline/feedback.json?feedback_type=NotRelevant&action_metadata=SRwW6oXZadPHiOczBBaAwPanEwE%3D' hasUndoAction: boolean; icon: string; // 'Frown' }; }; type TimelineAddEntries = { type: 'TimelineAddEntries'; entries: (TimelineItem | TimelineCursor | TimelineModule)[]; }; type TimelineTerminateTimeline = { type: 'TimelineTerminateTimeline', direction: 'Top', } type TimelineCursor = { entryId: string; // 'cursor-top-1867041249938530657' sortIndex: string; // '1867231621095096312' content: { __typename: 'TimelineTimelineCursor'; value: string; // 'DACBCgABGedb4VyaJwuKbIIZ40cX3dYwGgaAAwAEAEEAA' cursorType: 'Top' | 'Bottom'; }; }; type TimelineItem = { entryId: string; // 'tweet-1867041249938530657' sortIndex: string; // '1867231621095096312' content: { __typename: 'TimelineTimelineItem'; itemContent: TimelineTweet; feedbackInfo: { feedbackKeys: ActionKey[]; // ['-1378668161'] }; }; }; type TimelineModule = { entryId: string; // 'conversationthread-1867041249938530657' sortIndex: string; // '1867231621095096312' content: { __typename: 'TimelineTimelineModule'; items: { entryId: string, // 'conversationthread-1867041249938530657-tweet-1867041249938530657' item: TimelineTweet, }[], // Comments to the tweets are also tweets displayType: 'VerticalConversation', }; }; type TimelineTweet = { __typename: 'TimelineTweet'; tweet_results: { result: Tweet; }; }; type Tweet = { __typename: 'Tweet'; core: { user_results: { result: User; }; }; views: { count: string; // '13763' }; legacy: { bookmark_count: number; // 358 created_at: string; // 'Tue Dec 10 17:41:28 +0000 2024' conversation_id_str: string; // '1867041249938530657' display_text_range: number[]; // [0, 58] favorite_count: number; // 151 full_text: string; // "How I'd promote my startup, if I had 0 followers (Part 1)" lang: string; // 'en' quote_count: number; reply_count: number; retweet_count: number; user_id_str: string; // '1867041249938530657' id_str: string; // '1867041249938530657' entities: { media: Media[]; hashtags: Hashtag[]; urls: Url[]; user_mentions: UserMention[]; }; }; }; type User = { __typename: 'User'; id: string; // 'VXNlcjoxNDUxM4ADSG44MTA4NDc4OTc2' rest_id: string; // '1867041249938530657' is_blue_verified: boolean; profile_image_shape: 'Circle'; // ... legacy: { following: boolean; created_at: string; // 'Thu Oct 21 09:30:37 +0000 2021' description: string; // 'I help startup founders double their MRR with outside-the-box marketing cheat sheets' favourites_count: number; // 22195 followers_count: number; // 25658 friends_count: number; location: string; // 'San Francisco' media_count: number; name: string; // 'John Doe' profile_banner_url: string; // 'https://pbs.twimg.com/profile_banners/4863509452891265813/4863509' profile_image_url_https: string; // 'https://pbs.twimg.com/profile_images/4863509452891265813/4863509_normal.jpg' screen_name: string; // 'johndoe' url: string; // 'https://t.co/dgTEddFGDd' verified: boolean; }; }; type Media = { display_url: string; // 'pic.x.com/X7823zS3sNU' expanded_url: string; // 'https://x.com/johndoe/status/1867041249938530657/video/1' ext_alt_text: string; // 'Image of two bridges.' id_str: string; // '1867041249938530657' indices: number[]; // [93, 116] media_key: string; // '13_2866509231399826944' media_url_https: string; // 'https://pbs.twimg.com/profile_images/1867041249938530657/4863509_normal.jpg' source_status_id_str: string; // '1867041249938530657' source_user_id_str: string; // '1867041249938530657' type: string; // 'video' url: string; // 'https://t.co/X78dBgtrsNU' features: { large: { faces: FaceGeometry[] }; medium: { faces: FaceGeometry[] }; small: { faces: FaceGeometry[] }; orig: { faces: FaceGeometry[] }; }; sizes: { large: MediaSize; medium: MediaSize; small: MediaSize; thumb: MediaSize; }; video_info: VideoInfo[]; }; type UserMention = { id_str: string; // '98008038' name: string; // 'Yann LeCun' screen_name: string; // 'ylecun' indices: number[]; // [115, 122] }; type Hashtag = { indices: number[]; // [257, 263] text: string; }; type Url = { display_url: string; // 'google.com' expanded_url: string; // 'http://google.com' url: string; // 'https://t.co/nZh3aF0Aw6' indices: number[]; // [102, 125] }; type VideoInfo = { aspect_ratio: number[]; // [427, 240] duration_millis: number; // 20000 variants: { bitrate?: number; // 288000 content_type?: string; // 'application/x-mpegURL' | 'video/mp4' | ... url: string; // 'https://video.twimg.com/amplify_video/18665094345456w6944/pl/-ItQau_LRWedR-W7.m3u8?tag=14' }; }; type FaceGeometry = { x: number; y: number; h: number; w: number }; type MediaSize = { h: number; w: number; resize: 'fit' | 'crop' }; type ActionKey = string;