Add experimental user search parser
This commit is contained in:
parent
49a2fbb070
commit
4738ec3385
8 changed files with 74 additions and 33 deletions
|
@ -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)
|
||||
|
|
28
src/experimental/parser/timeline.nim
Normal file
28
src/experimental/parser/timeline.nim
Normal file
|
@ -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(
|
||||
|
|
23
src/experimental/types/timeline.nim
Normal file
23
src/experimental/types/timeline.nim
Normal file
|
@ -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 a new issue