search.nim 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. import httpclient, asyncdispatch, htmlparser
  2. import strutils, json, xmltree, uri
  3. import ".."/[types, parser, parserutils, query]
  4. import utils, consts, timeline
  5. proc getResult*[T](json: JsonNode; query: Query; after: string): Result[T] =
  6. if json == nil: return Result[T](beginning: true, query: query)
  7. Result[T](
  8. hasMore: json.getOrDefault("has_more_items").getBool(false),
  9. maxId: json.getOrDefault("max_position").getStr(""),
  10. minId: json.getOrDefault("min_position").getStr("").cleanPos(),
  11. query: query,
  12. beginning: after.len == 0
  13. )
  14. proc getSearch*[T](query: Query; after, agent: string): Future[Result[T]] {.async.} =
  15. let
  16. kind = if query.kind == userSearch: "users" else: "tweets"
  17. pos = when T is Tweet: genPos(after) else: after
  18. param = genQueryParam(query)
  19. encoded = encodeUrl(param, usePlus=false)
  20. headers = newHttpHeaders({
  21. "Accept": jsonAccept,
  22. "Referer": $(base / ("search?f=$1&q=$2&src=typd" % [kind, encoded])),
  23. "User-Agent": agent,
  24. "X-Requested-With": "XMLHttpRequest",
  25. "Authority": "twitter.com",
  26. "Accept-Language": lang
  27. })
  28. params = {
  29. "f": kind,
  30. "vertical": "default",
  31. "q": param,
  32. "src": "typd",
  33. "include_available_features": "1",
  34. "include_entities": "1",
  35. "max_position": if pos.len > 0: pos else: "0",
  36. "reset_error_state": "false"
  37. }
  38. if param in ["include:nativeretweets", "-filter:nativeretweets", ""]:
  39. return Result[T](query: query, beginning: true)
  40. let json = await fetchJson(base / searchUrl ? params, headers)
  41. result = getResult[T](json, query, after)
  42. if json == nil or not json.hasKey("items_html"): return
  43. when T is Tweet:
  44. result = await finishTimeline(json, query, after, agent)
  45. elif T is Profile:
  46. let html = json["items_html"].to(string)
  47. result.hasMore = html != "\n"
  48. for p in parseHtml(html).selectAll(".js-stream-item"):
  49. result.content.add parsePopupProfile(p, ".ProfileCard")