api.nim 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. # SPDX-License-Identifier: AGPL-3.0-only
  2. import asyncdispatch, httpclient, uri, strutils, sequtils, sugar
  3. import packedjson
  4. import types, query, formatters, consts, apiutils, parser
  5. import experimental/parser as newParser
  6. proc getGraphUser*(username: string): Future[User] {.async.} =
  7. if username.len == 0: return
  8. let
  9. variables = """{"screen_name": "$1"}""" % username
  10. params = {"variables": variables, "features": gqlFeatures}
  11. js = await fetchRaw(graphUser ? params, Api.userScreenName)
  12. result = parseGraphUser(js)
  13. proc getGraphUserById*(id: string): Future[User] {.async.} =
  14. if id.len == 0 or id.any(c => not c.isDigit): return
  15. let
  16. variables = """{"rest_id": "$1"}""" % id
  17. params = {"variables": variables, "features": gqlFeatures}
  18. js = await fetchRaw(graphUserById ? params, Api.userRestId)
  19. result = parseGraphUser(js)
  20. proc getGraphUserTweets*(id: string; kind: TimelineKind; after=""): Future[Profile] {.async.} =
  21. if id.len == 0: return
  22. let
  23. cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: ""
  24. variables = userTweetsVariables % [id, cursor]
  25. params = {"variables": variables, "features": gqlFeatures}
  26. (url, apiId) = case kind
  27. of TimelineKind.tweets: (graphUserTweets, Api.userTweets)
  28. of TimelineKind.replies: (graphUserTweetsAndReplies, Api.userTweetsAndReplies)
  29. of TimelineKind.media: (graphUserMedia, Api.userMedia)
  30. js = await fetch(url ? params, apiId)
  31. result = parseGraphTimeline(js, "user", after)
  32. proc getGraphListTweets*(id: string; after=""): Future[Timeline] {.async.} =
  33. if id.len == 0: return
  34. let
  35. cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: ""
  36. variables = listTweetsVariables % [id, cursor]
  37. params = {"variables": variables, "features": gqlFeatures}
  38. js = await fetch(graphListTweets ? params, Api.listTweets)
  39. result = parseGraphTimeline(js, "list", after).tweets
  40. proc getGraphListBySlug*(name, list: string): Future[List] {.async.} =
  41. let
  42. variables = %*{"screenName": name, "listSlug": list}
  43. params = {"variables": $variables, "features": gqlFeatures}
  44. result = parseGraphList(await fetch(graphListBySlug ? params, Api.listBySlug))
  45. proc getGraphList*(id: string): Future[List] {.async.} =
  46. let
  47. variables = """{"listId": "$1"}""" % id
  48. params = {"variables": variables, "features": gqlFeatures}
  49. result = parseGraphList(await fetch(graphListById ? params, Api.list))
  50. proc getGraphListMembers*(list: List; after=""): Future[Result[User]] {.async.} =
  51. if list.id.len == 0: return
  52. var
  53. variables = %*{
  54. "listId": list.id,
  55. "withBirdwatchPivots": false,
  56. "withDownvotePerspective": false,
  57. "withReactionsMetadata": false,
  58. "withReactionsPerspective": false
  59. }
  60. if after.len > 0:
  61. variables["cursor"] = % after
  62. let url = graphListMembers ? {"variables": $variables, "features": gqlFeatures}
  63. result = parseGraphListMembers(await fetchRaw(url, Api.listMembers), after)
  64. proc getGraphTweetResult*(id: string): Future[Tweet] {.async.} =
  65. if id.len == 0: return
  66. let
  67. variables = """{"rest_id": "$1"}""" % id
  68. params = {"variables": variables, "features": gqlFeatures}
  69. js = await fetch(graphTweetResult ? params, Api.tweetResult)
  70. result = parseGraphTweetResult(js)
  71. proc getGraphTweet(id: string; after=""): Future[Conversation] {.async.} =
  72. if id.len == 0: return
  73. let
  74. cursor = if after.len > 0: "\"cursor\":\"$1\"," % after else: ""
  75. variables = tweetVariables % [id, cursor]
  76. params = {"variables": variables, "features": gqlFeatures}
  77. js = await fetch(graphTweet ? params, Api.tweetDetail)
  78. result = parseGraphConversation(js, id)
  79. proc getReplies*(id, after: string): Future[Result[Chain]] {.async.} =
  80. result = (await getGraphTweet(id, after)).replies
  81. result.beginning = after.len == 0
  82. proc getTweet*(id: string; after=""): Future[Conversation] {.async.} =
  83. result = await getGraphTweet(id)
  84. if after.len > 0:
  85. result.replies = await getReplies(id, after)
  86. proc getGraphTweetSearch*(query: Query; after=""): Future[Timeline] {.async.} =
  87. let q = genQueryParam(query)
  88. if q.len == 0 or q == emptyQuery:
  89. return Timeline(query: query, beginning: true)
  90. var
  91. variables = %*{
  92. "rawQuery": q,
  93. "count": 20,
  94. "product": "Latest",
  95. "withDownvotePerspective": false,
  96. "withReactionsMetadata": false,
  97. "withReactionsPerspective": false
  98. }
  99. if after.len > 0:
  100. variables["cursor"] = % after
  101. let url = graphSearchTimeline ? {"variables": $variables, "features": gqlFeatures}
  102. result = parseGraphSearch[Tweets](await fetch(url, Api.search), after)
  103. result.query = query
  104. proc getGraphUserSearch*(query: Query; after=""): Future[Result[User]] {.async.} =
  105. if query.text.len == 0:
  106. return Result[User](query: query, beginning: true)
  107. var
  108. variables = %*{
  109. "rawQuery": query.text,
  110. "count": 20,
  111. "product": "People",
  112. "withDownvotePerspective": false,
  113. "withReactionsMetadata": false,
  114. "withReactionsPerspective": false
  115. }
  116. if after.len > 0:
  117. variables["cursor"] = % after
  118. result.beginning = false
  119. let url = graphSearchTimeline ? {"variables": $variables, "features": gqlFeatures}
  120. result = parseGraphSearch[User](await fetch(url, Api.search), after)
  121. result.query = query
  122. proc getPhotoRail*(id: string): Future[PhotoRail] {.async.} =
  123. if id.len == 0: return
  124. let
  125. variables = userTweetsVariables % [id, ""]
  126. params = {"variables": variables, "features": gqlFeatures}
  127. url = graphUserMedia ? params
  128. result = parseGraphPhotoRail(await fetch(url, Api.userMedia))
  129. proc resolve*(url: string; prefs: Prefs): Future[string] {.async.} =
  130. let client = newAsyncHttpClient(maxRedirects=0)
  131. try:
  132. let resp = await client.request(url, HttpHead)
  133. result = resp.headers["location"].replaceUrls(prefs)
  134. except:
  135. discard
  136. finally:
  137. client.close()