Add experimental user search parser
This commit is contained in:
		
							parent
							
								
									49a2fbb070
								
							
						
					
					
						commit
						4738ec3385
					
				| 
						 | 
				
			
			@ -3,6 +3,7 @@ import asyncdispatch, httpclient, uri, strutils, sequtils, sugar
 | 
			
		|||
import packedjson
 | 
			
		||||
import types, query, formatters, consts, apiutils, parser
 | 
			
		||||
import experimental/parser/[user, graphql]
 | 
			
		||||
import experimental/parser/timeline as timelineParser
 | 
			
		||||
 | 
			
		||||
proc getGraphUser*(id: string): Future[User] {.async.} =
 | 
			
		||||
  if id.len == 0 or id.any(c => not c.isDigit): return
 | 
			
		||||
| 
						 | 
				
			
			@ -85,10 +86,12 @@ proc getSearch*[T](query: Query; after=""): Future[Result[T]] {.async.} =
 | 
			
		|||
    const
 | 
			
		||||
      searchMode = ("result_filter", "user")
 | 
			
		||||
      parse = parseUsers
 | 
			
		||||
      fetchFunc = fetchRaw
 | 
			
		||||
  else:
 | 
			
		||||
    const
 | 
			
		||||
      searchMode = ("tweet_search_mode", "live")
 | 
			
		||||
      parse = parseTimeline
 | 
			
		||||
      fetchFunc = fetch
 | 
			
		||||
 | 
			
		||||
  let q = genQueryParam(query)
 | 
			
		||||
  if q.len == 0 or q == emptyQuery:
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +99,7 @@ proc getSearch*[T](query: Query; after=""): Future[Result[T]] {.async.} =
 | 
			
		|||
 | 
			
		||||
  let url = search ? genParams(searchParams & @[("q", q), searchMode], after)
 | 
			
		||||
  try:
 | 
			
		||||
    result = parse(await fetch(url, Api.search), after)
 | 
			
		||||
    result = parse(await fetchFunc(url, Api.search), after)
 | 
			
		||||
    result.query = query
 | 
			
		||||
  except InternalError:
 | 
			
		||||
    return Result[T](beginning: true, query: query)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
import std/[strutils, tables]
 | 
			
		||||
import jsony
 | 
			
		||||
import user, ../types/timeline
 | 
			
		||||
from ../../types import Result, User
 | 
			
		||||
 | 
			
		||||
proc getId(id: string): string {.inline.} =
 | 
			
		||||
  let start = id.rfind("-")
 | 
			
		||||
  if start < 0: return id
 | 
			
		||||
  id[start + 1 ..< id.len]
 | 
			
		||||
 | 
			
		||||
proc parseUsers*(json: string; after=""): Result[User] =
 | 
			
		||||
  result = Result[User](beginning: after.len == 0)
 | 
			
		||||
 | 
			
		||||
  let raw = json.fromJson(Search)
 | 
			
		||||
  if raw.timeline.instructions.len == 0:
 | 
			
		||||
    return
 | 
			
		||||
 | 
			
		||||
  for e in raw.timeline.instructions[0].addEntries.entries:
 | 
			
		||||
    let id = e.entryId.getId
 | 
			
		||||
    if e.entryId.startsWith("user"):
 | 
			
		||||
      if id in raw.globalObjects.users:
 | 
			
		||||
        result.content.add toUser raw.globalObjects.users[id]
 | 
			
		||||
    elif e.entryId.startsWith("cursor"):
 | 
			
		||||
      let cursor = e.content.operation.cursor
 | 
			
		||||
      if cursor.cursorType == "Top":
 | 
			
		||||
        result.top = cursor.value
 | 
			
		||||
      elif cursor.cursorType == "Bottom":
 | 
			
		||||
        result.bottom = cursor.value
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
import std/[algorithm, unicode, re, strutils, strformat]
 | 
			
		||||
import std/[algorithm, unicode, re, strutils, strformat, options]
 | 
			
		||||
import jsony
 | 
			
		||||
import utils, slices
 | 
			
		||||
import ../types/user as userType
 | 
			
		||||
| 
						 | 
				
			
			@ -38,9 +38,11 @@ proc getBanner(user: RawUser): string =
 | 
			
		|||
  if user.profileLinkColor.len > 0:
 | 
			
		||||
    return '#' & user.profileLinkColor
 | 
			
		||||
 | 
			
		||||
  if user.profileImageExtensions.mediaColor.r.ok.palette.len > 0:
 | 
			
		||||
    let color = user.profileImageExtensions.mediaColor.r.ok.palette[0].rgb
 | 
			
		||||
    return &"#{color.red:02x}{color.green:02x}{color.blue:02x}"
 | 
			
		||||
  if user.profileImageExtensions.isSome:
 | 
			
		||||
    let ext = get(user.profileImageExtensions)
 | 
			
		||||
    if ext.mediaColor.r.ok.palette.len > 0:
 | 
			
		||||
      let color = ext.mediaColor.r.ok.palette[0].rgb
 | 
			
		||||
      return &"#{color.red:02x}{color.green:02x}{color.blue:02x}"
 | 
			
		||||
 | 
			
		||||
proc toUser*(raw: RawUser): User =
 | 
			
		||||
  result = User(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
import std/tables
 | 
			
		||||
import user
 | 
			
		||||
 | 
			
		||||
type
 | 
			
		||||
  Search* = object
 | 
			
		||||
    globalObjects*: GlobalObjects
 | 
			
		||||
    timeline*: Timeline
 | 
			
		||||
 | 
			
		||||
  GlobalObjects = object
 | 
			
		||||
    users*: Table[string, RawUser]
 | 
			
		||||
 | 
			
		||||
  Timeline = object
 | 
			
		||||
    instructions*: seq[Instructions]
 | 
			
		||||
 | 
			
		||||
  Instructions = object
 | 
			
		||||
    addEntries*: tuple[entries: seq[Entry]]
 | 
			
		||||
 | 
			
		||||
  Entry = object
 | 
			
		||||
    entryId*: string
 | 
			
		||||
    content*: tuple[operation: Operation]
 | 
			
		||||
 | 
			
		||||
  Operation = object
 | 
			
		||||
    cursor*: tuple[value, cursorType: string]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
import options
 | 
			
		||||
import common
 | 
			
		||||
 | 
			
		||||
type
 | 
			
		||||
| 
						 | 
				
			
			@ -16,10 +17,10 @@ type
 | 
			
		|||
    mediaCount*: int
 | 
			
		||||
    verified*: bool
 | 
			
		||||
    protected*: bool
 | 
			
		||||
    profileLinkColor*: string
 | 
			
		||||
    profileBannerUrl*: string
 | 
			
		||||
    profileImageUrlHttps*: string
 | 
			
		||||
    profileImageExtensions*: ImageExtensions
 | 
			
		||||
    profileLinkColor*: string
 | 
			
		||||
    profileImageExtensions*: Option[ImageExtensions]
 | 
			
		||||
    pinnedTweetIdsStr*: seq[string]
 | 
			
		||||
 | 
			
		||||
  Entities* = object
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -366,26 +366,6 @@ proc parseInstructions[T](res: var Result[T]; global: GlobalObjects; js: JsonNod
 | 
			
		|||
      elif "bottom" in r{"entryId"}.getStr:
 | 
			
		||||
        res.bottom = r.getCursor
 | 
			
		||||
 | 
			
		||||
proc parseUsers*(js: JsonNode; after=""): Result[User] =
 | 
			
		||||
  result = Result[User](beginning: after.len == 0)
 | 
			
		||||
  let global = parseGlobalObjects(? js)
 | 
			
		||||
 | 
			
		||||
  let instructions = ? js{"timeline", "instructions"}
 | 
			
		||||
  if instructions.len == 0: return
 | 
			
		||||
 | 
			
		||||
  result.parseInstructions(global, instructions)
 | 
			
		||||
 | 
			
		||||
  for e in instructions[0]{"addEntries", "entries"}:
 | 
			
		||||
    let entry = e{"entryId"}.getStr
 | 
			
		||||
    if "user-" in entry:
 | 
			
		||||
      let id = entry.getId
 | 
			
		||||
      if id in global.users:
 | 
			
		||||
        result.content.add global.users[id]
 | 
			
		||||
    elif "cursor-top" in entry:
 | 
			
		||||
      result.top = e.getCursor
 | 
			
		||||
    elif "cursor-bottom" in entry:
 | 
			
		||||
      result.bottom = e.getCursor
 | 
			
		||||
 | 
			
		||||
proc parseTimeline*(js: JsonNode; after=""): Timeline =
 | 
			
		||||
  result = Timeline(beginning: after.len == 0)
 | 
			
		||||
  let global = parseGlobalObjects(? js)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,12 +42,16 @@
 | 
			
		|||
    top: 50px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.profile-result .username {
 | 
			
		||||
    margin: 0 !important;
 | 
			
		||||
}
 | 
			
		||||
.profile-result {
 | 
			
		||||
    min-height: 54px;
 | 
			
		||||
 | 
			
		||||
.profile-result .tweet-header {
 | 
			
		||||
    margin-bottom: unset;
 | 
			
		||||
    .username {
 | 
			
		||||
        margin: 0 !important;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .tweet-header {
 | 
			
		||||
        margin-bottom: unset;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media(max-width: 700px) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,7 +52,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
 | 
			
		|||
  let opensearchUrl = getUrlPrefix(cfg) & "/opensearch"
 | 
			
		||||
 | 
			
		||||
  buildHtml(head):
 | 
			
		||||
    link(rel="stylesheet", type="text/css", href="/css/style.css?v=15")
 | 
			
		||||
    link(rel="stylesheet", type="text/css", href="/css/style.css?v=16")
 | 
			
		||||
    link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=2")
 | 
			
		||||
 | 
			
		||||
    if theme.len > 0:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue