Misc. changes
This commit is contained in:
parent
4c928fa8b0
commit
81d6d1ac7f
|
@ -28,6 +28,7 @@ proc stripHtml*(text: string): string =
|
||||||
for el in html.findAll("a"):
|
for el in html.findAll("a"):
|
||||||
let link = el.attr("href")
|
let link = el.attr("href")
|
||||||
if "http" in link:
|
if "http" in link:
|
||||||
|
if el.len == 0: continue
|
||||||
el[0].text = link
|
el[0].text = link
|
||||||
html.innerText()
|
html.innerText()
|
||||||
|
|
||||||
|
@ -94,9 +95,32 @@ proc getRfc822Time*(tweet: Tweet): string =
|
||||||
proc getTweetTime*(tweet: Tweet): string =
|
proc getTweetTime*(tweet: Tweet): string =
|
||||||
tweet.time.format("h:mm tt' · 'MMM d', 'YYYY")
|
tweet.time.format("h:mm tt' · 'MMM d', 'YYYY")
|
||||||
|
|
||||||
proc getLink*(tweet: Tweet | Quote; focus=true): string =
|
proc getShortTime*(tweet: Tweet): string =
|
||||||
|
let
|
||||||
|
now = now().utc
|
||||||
|
then = tweet.time.utc
|
||||||
|
since = now - then
|
||||||
|
|
||||||
|
if now.year != then.year:
|
||||||
|
result = tweet.time.format("d MMM yyyy")
|
||||||
|
elif since.inDays >= 1:
|
||||||
|
result = tweet.time.format("MMM d")
|
||||||
|
elif since.inHours >= 1:
|
||||||
|
result = $since.inHours & "h"
|
||||||
|
elif since.inMinutes >= 1:
|
||||||
|
result = $since.inMinutes & "m"
|
||||||
|
elif since.inSeconds > 1:
|
||||||
|
result = $since.inSeconds & "s"
|
||||||
|
else:
|
||||||
|
# this shouldn't happen, but just in case
|
||||||
|
result = "now"
|
||||||
|
|
||||||
|
proc getLink*(tweet: Tweet; focus=true): string =
|
||||||
if tweet.id == 0: return
|
if tweet.id == 0: return
|
||||||
result = &"/{tweet.profile.username}/status/{tweet.id}"
|
var username = tweet.profile.username
|
||||||
|
if username.len == 0:
|
||||||
|
username = "i"
|
||||||
|
result = &"/{username}/status/{tweet.id}"
|
||||||
if focus: result &= "#m"
|
if focus: result &= "#m"
|
||||||
|
|
||||||
proc getTombstone*(text: string): string =
|
proc getTombstone*(text: string): string =
|
||||||
|
@ -114,8 +138,7 @@ proc getTwitterLink*(path: string; params: Table[string, string]): string =
|
||||||
let p = {
|
let p = {
|
||||||
"f": $query.kind,
|
"f": $query.kind,
|
||||||
"q": genQueryParam(query),
|
"q": genQueryParam(query),
|
||||||
"src": "typd",
|
"src": "typed_query"
|
||||||
"max_position": params.getOrDefault("max_position", "0")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result = $(parseUri("https://twitter.com") / path ? p)
|
result = $(parseUri("https://twitter.com") / path ? p)
|
||||||
|
|
|
@ -3,7 +3,7 @@ from net import Port
|
||||||
|
|
||||||
import jester
|
import jester
|
||||||
|
|
||||||
import types, config, prefs, formatters, cache
|
import types, config, prefs, formatters, redis_cache, tokens
|
||||||
import views/[general, about]
|
import views/[general, about]
|
||||||
import routes/[
|
import routes/[
|
||||||
preferences, timeline, status, media, search, rss, list,
|
preferences, timeline, status, media, search, rss, list,
|
||||||
|
@ -13,8 +13,10 @@ const configPath {.strdefine.} = "./nitter.conf"
|
||||||
let (cfg, fullCfg) = getConfig(configPath)
|
let (cfg, fullCfg) = getConfig(configPath)
|
||||||
|
|
||||||
updateDefaultPrefs(fullCfg)
|
updateDefaultPrefs(fullCfg)
|
||||||
|
setCacheTimes(cfg)
|
||||||
setHmacKey(cfg.hmacKey)
|
setHmacKey(cfg.hmacKey)
|
||||||
|
initRedisPool(cfg)
|
||||||
|
asyncCheck initTokenPool(cfg)
|
||||||
|
|
||||||
createUnsupportedRouter(cfg)
|
createUnsupportedRouter(cfg)
|
||||||
createResolverRouter(cfg)
|
createResolverRouter(cfg)
|
||||||
|
@ -27,8 +29,6 @@ createMediaRouter(cfg)
|
||||||
createEmbedRouter(cfg)
|
createEmbedRouter(cfg)
|
||||||
createRssRouter(cfg)
|
createRssRouter(cfg)
|
||||||
|
|
||||||
asyncCheck cacheCleaner()
|
|
||||||
|
|
||||||
settings:
|
settings:
|
||||||
port = Port(cfg.port)
|
port = Port(cfg.port)
|
||||||
staticDir = cfg.staticDir
|
staticDir = cfg.staticDir
|
||||||
|
|
|
@ -8,11 +8,14 @@ type
|
||||||
name*: string
|
name*: string
|
||||||
label*: string
|
label*: string
|
||||||
kind*: PrefKind
|
kind*: PrefKind
|
||||||
options*: seq[string]
|
# checkbox
|
||||||
placeholder*: string
|
|
||||||
defaultState*: bool
|
defaultState*: bool
|
||||||
|
# select
|
||||||
defaultOption*: string
|
defaultOption*: string
|
||||||
|
options*: seq[string]
|
||||||
|
# input
|
||||||
defaultInput*: string
|
defaultInput*: string
|
||||||
|
placeholder*: string
|
||||||
|
|
||||||
PrefList* = OrderedTable[string, seq[Pref]]
|
PrefList* = OrderedTable[string, seq[Pref]]
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import strutils, strformat, sequtils, tables, uri
|
||||||
import types
|
import types
|
||||||
|
|
||||||
const
|
const
|
||||||
separators = @["AND", "OR"]
|
# separators = @["AND", "OR"]
|
||||||
validFilters* = @[
|
validFilters* = @[
|
||||||
"media", "images", "twimg", "videos",
|
"media", "images", "twimg", "videos",
|
||||||
"native_video", "consumer_video", "pro_video",
|
"native_video", "consumer_video", "pro_video",
|
||||||
|
|
118
src/types.nim
118
src/types.nim
|
@ -1,17 +1,30 @@
|
||||||
import times, sequtils, options
|
import times, sequtils, options, tables
|
||||||
import norm/sqlite
|
|
||||||
|
|
||||||
import prefs_impl
|
import prefs_impl
|
||||||
|
|
||||||
genPrefsType()
|
genPrefsType()
|
||||||
|
|
||||||
type
|
type
|
||||||
VideoType* = enum
|
Token* = ref object
|
||||||
vmap, m3u8, mp4
|
tok*: string
|
||||||
|
limit*: int
|
||||||
|
remaining*: int
|
||||||
|
reset*: Time
|
||||||
|
init*: Time
|
||||||
|
# agent*: string
|
||||||
|
|
||||||
|
Error* = enum
|
||||||
|
protectedUser = 22
|
||||||
|
couldntAuth = 32
|
||||||
|
doesntExist = 34
|
||||||
|
notFound = 50
|
||||||
|
suspended = 63
|
||||||
|
invalidToken = 89
|
||||||
|
listIdOrSlug = 112
|
||||||
|
forbidden = 200
|
||||||
|
noCsrf = 353
|
||||||
|
|
||||||
dbTypes:
|
|
||||||
type
|
|
||||||
Profile* = object
|
Profile* = object
|
||||||
|
id*: string
|
||||||
username*: string
|
username*: string
|
||||||
fullname*: string
|
fullname*: string
|
||||||
lowername*: string
|
lowername*: string
|
||||||
|
@ -28,16 +41,17 @@ dbTypes:
|
||||||
verified*: bool
|
verified*: bool
|
||||||
protected*: bool
|
protected*: bool
|
||||||
suspended*: bool
|
suspended*: bool
|
||||||
joinDate* {.
|
joinDate*: Time
|
||||||
dbType: "INTEGER"
|
|
||||||
parseIt: it.i.fromUnix()
|
VideoType* = enum
|
||||||
formatIt: dbValue(it.toUnix())
|
m3u8 = "application/x-mpegURL"
|
||||||
.}: Time
|
mp4 = "video/mp4"
|
||||||
updated* {.
|
vmap = "video/vmap"
|
||||||
dbType: "INTEGER"
|
|
||||||
parseIt: it.i.fromUnix()
|
VideoVariant* = object
|
||||||
formatIt: dbValue(getTime().toUnix())
|
videoType*: VideoType
|
||||||
.}: Time
|
url*: string
|
||||||
|
bitrate*: int
|
||||||
|
|
||||||
Video* = object
|
Video* = object
|
||||||
videoId*: string
|
videoId*: string
|
||||||
|
@ -50,19 +64,9 @@ dbTypes:
|
||||||
reason*: string
|
reason*: string
|
||||||
title*: string
|
title*: string
|
||||||
description*: string
|
description*: string
|
||||||
playbackType* {.
|
playbackType*: VideoType
|
||||||
dbType: "STRING"
|
variants*: seq[VideoVariant]
|
||||||
parseIt: parseEnum[VideoType](it.s)
|
|
||||||
formatIt: dbValue($it)
|
|
||||||
.}: VideoType
|
|
||||||
updated* {.
|
|
||||||
dbType: "INTEGER"
|
|
||||||
parseIt: it.i.fromUnix()
|
|
||||||
formatIt: dbValue(getTime().toUnix())
|
|
||||||
.}: Time
|
|
||||||
|
|
||||||
|
|
||||||
type
|
|
||||||
QueryKind* = enum
|
QueryKind* = enum
|
||||||
posts, replies, media, users, tweets, userList
|
posts, replies, media, users, tweets, userList
|
||||||
|
|
||||||
|
@ -92,18 +96,20 @@ type
|
||||||
Poll* = object
|
Poll* = object
|
||||||
options*: seq[string]
|
options*: seq[string]
|
||||||
values*: seq[int]
|
values*: seq[int]
|
||||||
votes*: string
|
votes*: int
|
||||||
status*: string
|
|
||||||
leader*: int
|
leader*: int
|
||||||
|
status*: string
|
||||||
|
|
||||||
CardKind* = enum
|
CardKind* = enum
|
||||||
|
player = "player"
|
||||||
summary = "summary"
|
summary = "summary"
|
||||||
summaryLarge = "summary_large_image"
|
summaryLarge = "summary_large_image"
|
||||||
promoWebsite = "promo_website"
|
promoWebsite = "promo_website"
|
||||||
promoVideo = "promo_video_website"
|
promoVideo = "promo_video_website"
|
||||||
promoVideoConvo = "promo_video_convo"
|
promoVideoConvo = "promo_video_convo"
|
||||||
player = "player"
|
|
||||||
liveEvent = "live_event"
|
liveEvent = "live_event"
|
||||||
|
broadcast = "broadcast"
|
||||||
|
periscope = "periscope_broadcast"
|
||||||
|
|
||||||
Card* = object
|
Card* = object
|
||||||
kind*: CardKind
|
kind*: CardKind
|
||||||
|
@ -113,25 +119,9 @@ type
|
||||||
title*: string
|
title*: string
|
||||||
dest*: string
|
dest*: string
|
||||||
text*: string
|
text*: string
|
||||||
image*: Option[string]
|
image*: string
|
||||||
video*: Option[Video]
|
video*: Option[Video]
|
||||||
|
|
||||||
Quote* = object
|
|
||||||
id*: int64
|
|
||||||
profile*: Profile
|
|
||||||
text*: string
|
|
||||||
reply*: seq[string]
|
|
||||||
hasThread*: bool
|
|
||||||
sensitive*: bool
|
|
||||||
available*: bool
|
|
||||||
tombstone*: string
|
|
||||||
thumb*: string
|
|
||||||
badge*: string
|
|
||||||
|
|
||||||
Retweet* = object
|
|
||||||
by*: string
|
|
||||||
id*: int64
|
|
||||||
|
|
||||||
TweetStats* = object
|
TweetStats* = object
|
||||||
replies*: int
|
replies*: int
|
||||||
retweets*: int
|
retweets*: int
|
||||||
|
@ -140,10 +130,10 @@ type
|
||||||
Tweet* = ref object
|
Tweet* = ref object
|
||||||
id*: int64
|
id*: int64
|
||||||
threadId*: int64
|
threadId*: int64
|
||||||
|
replyId*: int64
|
||||||
profile*: Profile
|
profile*: Profile
|
||||||
text*: string
|
text*: string
|
||||||
time*: Time
|
time*: Time
|
||||||
shortTime*: string
|
|
||||||
reply*: seq[string]
|
reply*: seq[string]
|
||||||
pinned*: bool
|
pinned*: bool
|
||||||
hasThread*: bool
|
hasThread*: bool
|
||||||
|
@ -151,27 +141,26 @@ type
|
||||||
tombstone*: string
|
tombstone*: string
|
||||||
location*: string
|
location*: string
|
||||||
stats*: TweetStats
|
stats*: TweetStats
|
||||||
retweet*: Option[Retweet]
|
retweet*: Option[Tweet]
|
||||||
attribution*: Option[Profile]
|
attribution*: Option[Profile]
|
||||||
mediaTags*: seq[Profile]
|
mediaTags*: seq[Profile]
|
||||||
quote*: Option[Quote]
|
quote*: Option[Tweet]
|
||||||
card*: Option[Card]
|
card*: Option[Card]
|
||||||
|
poll*: Option[Poll]
|
||||||
gif*: Option[Gif]
|
gif*: Option[Gif]
|
||||||
video*: Option[Video]
|
video*: Option[Video]
|
||||||
photos*: seq[string]
|
photos*: seq[string]
|
||||||
poll*: Option[Poll]
|
|
||||||
|
|
||||||
Result*[T] = ref object
|
Result*[T] = object
|
||||||
content*: seq[T]
|
content*: seq[T]
|
||||||
minId*: string
|
top*, bottom*: string
|
||||||
maxId*: string
|
|
||||||
hasMore*: bool
|
|
||||||
beginning*: bool
|
beginning*: bool
|
||||||
query*: Query
|
query*: Query
|
||||||
|
|
||||||
Chain* = ref object
|
Chain* = object
|
||||||
content*: seq[Tweet]
|
content*: seq[Tweet]
|
||||||
more*: int64
|
more*: int64
|
||||||
|
cursor*: string
|
||||||
|
|
||||||
Conversation* = ref object
|
Conversation* = ref object
|
||||||
tweet*: Tweet
|
tweet*: Tweet
|
||||||
|
@ -181,6 +170,19 @@ type
|
||||||
|
|
||||||
Timeline* = Result[Tweet]
|
Timeline* = Result[Tweet]
|
||||||
|
|
||||||
|
List* = object
|
||||||
|
id*: string
|
||||||
|
name*: string
|
||||||
|
userId*: string
|
||||||
|
username*: string
|
||||||
|
description*: string
|
||||||
|
members*: int
|
||||||
|
banner*: string
|
||||||
|
|
||||||
|
GlobalObjects* = ref object
|
||||||
|
tweets*: Table[string, Tweet]
|
||||||
|
users*: Table[string, Profile]
|
||||||
|
|
||||||
Config* = ref object
|
Config* = ref object
|
||||||
address*: string
|
address*: string
|
||||||
port*: int
|
port*: int
|
||||||
|
|
|
@ -8,6 +8,7 @@ const
|
||||||
badPngExts = @["pngn", "png:", "png_", "_png"]
|
badPngExts = @["pngn", "png:", "png_", "_png"]
|
||||||
twitterDomains = @[
|
twitterDomains = @[
|
||||||
"twitter.com",
|
"twitter.com",
|
||||||
|
"pic.twitter.com",
|
||||||
"twimg.com",
|
"twimg.com",
|
||||||
"abs.twimg.com",
|
"abs.twimg.com",
|
||||||
"pbs.twimg.com",
|
"pbs.twimg.com",
|
||||||
|
|
Loading…
Reference in New Issue