mirror of
https://github.com/zedeus/nitter.git
synced 2026-04-15 18:22:11 -04:00
15
src/api.nim
15
src/api.nim
@@ -1,7 +1,7 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import asyncdispatch, httpclient, strutils, sequtils, sugar
|
import asyncdispatch, httpclient, strutils, sequtils, sugar
|
||||||
import packedjson
|
import packedjson
|
||||||
import types, query, formatters, consts, apiutils, parser
|
import types, query, formatters, consts, apiutils, parser, utils
|
||||||
import experimental/parser as newParser
|
import experimental/parser as newParser
|
||||||
|
|
||||||
# Helper to generate params object for GraphQL requests
|
# Helper to generate params object for GraphQL requests
|
||||||
@@ -146,7 +146,12 @@ proc getGraphEditHistory*(id: string): Future[EditHistory] {.async.} =
|
|||||||
result = parseGraphEditHistory(js, id)
|
result = parseGraphEditHistory(js, id)
|
||||||
|
|
||||||
proc getGraphTweetSearch*(query: Query; after=""): Future[Timeline] {.async.} =
|
proc getGraphTweetSearch*(query: Query; after=""): Future[Timeline] {.async.} =
|
||||||
let q = genQueryParam(query)
|
# workaround for #1372
|
||||||
|
let maxId =
|
||||||
|
if not after.startsWith("maxid:"): ""
|
||||||
|
else: validateNumber(after[6..^1])
|
||||||
|
|
||||||
|
let q = genQueryParam(query, maxId)
|
||||||
if q.len == 0 or q == emptyQuery:
|
if q.len == 0 or q == emptyQuery:
|
||||||
return Timeline(query: query, beginning: true)
|
return Timeline(query: query, beginning: true)
|
||||||
|
|
||||||
@@ -160,9 +165,9 @@ proc getGraphTweetSearch*(query: Query; after=""): Future[Timeline] {.async.} =
|
|||||||
"withReactionsMetadata": false,
|
"withReactionsMetadata": false,
|
||||||
"withReactionsPerspective": false
|
"withReactionsPerspective": false
|
||||||
}
|
}
|
||||||
if after.len > 0:
|
if after.len > 0 and maxId.len == 0:
|
||||||
variables["cursor"] = % after
|
variables["cursor"] = % after
|
||||||
let
|
let
|
||||||
url = apiReq(graphSearchTimeline, $variables)
|
url = apiReq(graphSearchTimeline, $variables)
|
||||||
js = await fetch(url)
|
js = await fetch(url)
|
||||||
result = parseGraphSearch[Tweets](js, after)
|
result = parseGraphSearch[Tweets](js, after)
|
||||||
@@ -170,7 +175,7 @@ proc getGraphTweetSearch*(query: Query; after=""): Future[Timeline] {.async.} =
|
|||||||
|
|
||||||
# when no more items are available the API just returns the last page in
|
# when no more items are available the API just returns the last page in
|
||||||
# full. this detects that and clears the page instead.
|
# full. this detects that and clears the page instead.
|
||||||
if after.len > 0 and result.bottom.len > 0 and
|
if after.len > 0 and result.bottom.len > 0 and maxId.len == 0 and
|
||||||
after[0..<64] == result.bottom[0..<64]:
|
after[0..<64] == result.bottom[0..<64]:
|
||||||
result.content.setLen(0)
|
result.content.setLen(0)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import strutils, strformat, sequtils, tables, uri
|
import strutils, strformat, sequtils, tables, uri
|
||||||
|
|
||||||
import types
|
import types, utils
|
||||||
|
|
||||||
const
|
const
|
||||||
validFilters* = @[
|
validFilters* = @[
|
||||||
@@ -17,11 +17,6 @@ template `@`(param: string): untyped =
|
|||||||
if param in pms: pms[param]
|
if param in pms: pms[param]
|
||||||
else: ""
|
else: ""
|
||||||
|
|
||||||
proc validateNumber(value: string): string =
|
|
||||||
if value.anyIt(not it.isDigit):
|
|
||||||
return ""
|
|
||||||
return value
|
|
||||||
|
|
||||||
proc initQuery*(pms: Table[string, string]; name=""): Query =
|
proc initQuery*(pms: Table[string, string]; name=""): Query =
|
||||||
result = Query(
|
result = Query(
|
||||||
kind: parseEnum[QueryKind](@"f", tweets),
|
kind: parseEnum[QueryKind](@"f", tweets),
|
||||||
@@ -50,7 +45,7 @@ proc getReplyQuery*(name: string): Query =
|
|||||||
fromUser: @[name]
|
fromUser: @[name]
|
||||||
)
|
)
|
||||||
|
|
||||||
proc genQueryParam*(query: Query): string =
|
proc genQueryParam*(query: Query; maxId=""): string =
|
||||||
var
|
var
|
||||||
filters: seq[string]
|
filters: seq[string]
|
||||||
param: string
|
param: string
|
||||||
@@ -58,12 +53,15 @@ proc genQueryParam*(query: Query): string =
|
|||||||
if query.kind == users:
|
if query.kind == users:
|
||||||
return query.text
|
return query.text
|
||||||
|
|
||||||
param = "("
|
|
||||||
for i, user in query.fromUser:
|
for i, user in query.fromUser:
|
||||||
|
if i == 0:
|
||||||
|
param = "("
|
||||||
|
|
||||||
param &= &"from:{user}"
|
param &= &"from:{user}"
|
||||||
if i < query.fromUser.high:
|
if i < query.fromUser.high:
|
||||||
param &= " OR "
|
param &= " OR "
|
||||||
param &= ")"
|
else:
|
||||||
|
param &= ")"
|
||||||
|
|
||||||
if query.fromUser.len > 0 and query.kind in {posts, media}:
|
if query.fromUser.len > 0 and query.kind in {posts, media}:
|
||||||
param &= " (filter:self_threads OR -filter:replies)"
|
param &= " (filter:self_threads OR -filter:replies)"
|
||||||
@@ -86,7 +84,7 @@ proc genQueryParam*(query: Query): string =
|
|||||||
|
|
||||||
if query.since.len > 0:
|
if query.since.len > 0:
|
||||||
result &= " since:" & query.since
|
result &= " since:" & query.since
|
||||||
if query.until.len > 0:
|
if query.until.len > 0 and maxId.len == 0:
|
||||||
result &= " until:" & query.until
|
result &= " until:" & query.until
|
||||||
if query.minLikes.len > 0:
|
if query.minLikes.len > 0:
|
||||||
result &= " min_faves:" & query.minLikes
|
result &= " min_faves:" & query.minLikes
|
||||||
@@ -96,6 +94,9 @@ proc genQueryParam*(query: Query): string =
|
|||||||
else:
|
else:
|
||||||
result = query.text
|
result = query.text
|
||||||
|
|
||||||
|
if result.len > 0 and maxId.len > 0:
|
||||||
|
result &= " max_id:" & maxId
|
||||||
|
|
||||||
proc genQueryUrl*(query: Query): string =
|
proc genQueryUrl*(query: Query): string =
|
||||||
if query.kind notin {tweets, users}: return
|
if query.kind notin {tweets, users}: return
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
import strutils, strformat, uri, tables, base64
|
import sequtils, strutils, strformat, uri, tables, base64
|
||||||
import nimcrypto
|
import nimcrypto
|
||||||
|
|
||||||
var
|
var
|
||||||
@@ -59,3 +59,8 @@ proc isTwitterUrl*(uri: Uri): bool =
|
|||||||
|
|
||||||
proc isTwitterUrl*(url: string): bool =
|
proc isTwitterUrl*(url: string): bool =
|
||||||
isTwitterUrl(parseUri(url))
|
isTwitterUrl(parseUri(url))
|
||||||
|
|
||||||
|
proc validateNumber*(value: string): string =
|
||||||
|
if value.anyIt(not it.isDigit):
|
||||||
|
return ""
|
||||||
|
return value
|
||||||
|
|||||||
@@ -11,6 +11,23 @@ proc getQuery(query: Query): string =
|
|||||||
if result.len > 0:
|
if result.len > 0:
|
||||||
result &= "&"
|
result &= "&"
|
||||||
|
|
||||||
|
proc getSearchMaxId(results: Timeline; path: string): string =
|
||||||
|
if results.query.kind != tweets or results.content.len == 0 or
|
||||||
|
results.query.until.len == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
let lastThread = results.content[^1]
|
||||||
|
if lastThread.len == 0 or lastThread[^1].id == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 2000000 is the minimum decrement to guarantee no result overlap
|
||||||
|
var maxId = lastThread[^1].id - 2_000_000'i64
|
||||||
|
if maxId <= 0:
|
||||||
|
maxId = lastThread[^1].id - 1
|
||||||
|
|
||||||
|
if maxId > 0:
|
||||||
|
return "maxid:" & $maxId
|
||||||
|
|
||||||
proc renderToTop*(focus="#"): VNode =
|
proc renderToTop*(focus="#"): VNode =
|
||||||
buildHtml(tdiv(class="top-ref")):
|
buildHtml(tdiv(class="top-ref")):
|
||||||
icon "down", href=focus
|
icon "down", href=focus
|
||||||
@@ -122,6 +139,9 @@ proc renderTimelineTweets*(results: Timeline; prefs: Prefs; path: string;
|
|||||||
else:
|
else:
|
||||||
renderThread(thread, prefs, path)
|
renderThread(thread, prefs, path)
|
||||||
|
|
||||||
if results.bottom.len > 0:
|
var cursor = getSearchMaxId(results, path)
|
||||||
|
if cursor.len > 0:
|
||||||
|
renderMore(results.query, cursor)
|
||||||
|
elif results.bottom.len > 0:
|
||||||
renderMore(results.query, results.bottom)
|
renderMore(results.query, results.bottom)
|
||||||
renderToTop()
|
renderToTop()
|
||||||
|
|||||||
Reference in New Issue
Block a user