Add support for business and gov verification

Also improve icon rendering on Firefox
This commit is contained in:
Zed 2023-11-25 10:06:12 +00:00
parent d6be08d093
commit f8254c2f0f
14 changed files with 59 additions and 31 deletions

View file

@ -29,12 +29,10 @@ const
"include_cards": "1",
"include_entities": "1",
"include_profile_interstitial_type": "0",
"include_quote_count": "1",
"include_reply_count": "1",
"include_user_entities": "1",
"include_ext_reply_count": "1",
"include_ext_is_blue_verified": "1",
# "include_ext_verified_type": "1",
"include_quote_count": "0",
"include_reply_count": "0",
"include_user_entities": "0",
"include_ext_reply_count": "0",
"include_ext_media_color": "0",
"cards_platform": "Web-13",
"tweet_mode": "extended",

View file

@ -1,7 +1,7 @@
import options
import jsony
import user, ../types/[graphuser, graphlistmembers]
from ../../types import User, Result, Query, QueryKind
from ../../types import User, VerifiedType, Result, Query, QueryKind
proc parseGraphUser*(json: string): User =
if json.len == 0 or json[0] != '{':
@ -14,7 +14,8 @@ proc parseGraphUser*(json: string): User =
result = raw.data.userResult.result.legacy
result.id = raw.data.userResult.result.restId
result.verified = result.verified or raw.data.userResult.result.isBlueVerified
if result.verifiedType == none and raw.data.userResult.result.isBlueVerified:
result.verifiedType = blue
proc parseGraphListMembers*(json, cursor: string): Result[User] =
result = Result[User](

View file

@ -1,7 +1,6 @@
import std/[options, tables, strutils, strformat, sugar]
import jsony
import user
import ../types/unifiedcard
import user, ../types/unifiedcard
from ../../types import Card, CardKind, Video
from ../../utils import twimg, https

View file

@ -56,7 +56,7 @@ proc toUser*(raw: RawUser): User =
tweets: raw.statusesCount,
likes: raw.favouritesCount,
media: raw.mediaCount,
verified: raw.verified or raw.extIsBlueVerified,
verifiedType: raw.verifiedType,
protected: raw.protected,
joinDate: parseTwitterDate(raw.createdAt),
banner: getBanner(raw),

View file

@ -1,5 +1,6 @@
import options
import common
from ../../types import VerifiedType
type
RawUser* = object
@ -15,8 +16,7 @@ type
favouritesCount*: int
statusesCount*: int
mediaCount*: int
verified*: bool
extIsBlueVerified*: bool
verifiedType*: VerifiedType
protected*: bool
profileLinkColor*: string
profileBannerUrl*: string

View file

@ -21,7 +21,7 @@ proc parseUser(js: JsonNode; id=""): User =
tweets: js{"statuses_count"}.getInt,
likes: js{"favourites_count"}.getInt,
media: js{"media_count"}.getInt,
verified: js{"verified"}.getBool or js{"ext_is_blue_verified"}.getBool,
verifiedType: parseEnum[VerifiedType](js{"verified_type"}.getStr("None")),
protected: js{"protected"}.getBool,
joinDate: js{"created_at"}.getTime
)
@ -34,8 +34,8 @@ proc parseGraphUser(js: JsonNode): User =
user = ? js{"user_results", "result"}
result = parseUser(user{"legacy"})
if "is_blue_verified" in user:
result.verified = user{"is_blue_verified"}.getBool()
if result.verifiedType == none and user{"is_blue_verified"}.getBool(false):
result.verifiedType = blue
proc parseGraphList*(js: JsonNode): List =
if js.isNull: return

View file

@ -52,6 +52,7 @@ proc initRedisPool*(cfg: Config) {.async.} =
await migrate("profileDates", "p:*")
await migrate("profileStats", "p:*")
await migrate("userType", "p:*")
await migrate("verifiedType", "p:*")
pool.withAcquire(r):
# optimize memory usage for user ID buckets

View file

@ -28,6 +28,8 @@ $more_replies_dots: #AD433B;
$error_red: #420A05;
$verified_blue: #1DA1F2;
$verified_business: #FAC82B;
$verified_government: #C1B6A4;
$icon_text: $fg_color;
$tab: $fg_color;

View file

@ -39,6 +39,8 @@ body {
--error_red: #{$error_red};
--verified_blue: #{$verified_blue};
--verified_business: #{$verified_business};
--verified_government: #{$verified_government};
--icon_text: #{$icon_text};
--tab: #{$fg_color};
@ -141,17 +143,30 @@ ul {
.verified-icon {
color: var(--icon_text);
background-color: var(--verified_blue);
border-radius: 50%;
flex-shrink: 0;
margin: 2px 0 3px 3px;
padding-top: 2px;
height: 12px;
padding-top: 3px;
height: 11px;
width: 14px;
font-size: 8px;
display: inline-block;
text-align: center;
vertical-align: middle;
&.blue {
background-color: var(--verified_blue);
}
&.business {
color: var(--bg_panel);
background-color: var(--verified_business);
}
&.government {
color: var(--bg_panel);
background-color: var(--verified_government);
}
}
@media(max-width: 600px) {

View file

@ -14,6 +14,8 @@
button {
margin: 0 2px 0 0;
height: 23px;
display: flex;
align-items: center;
}
.pref-input {

View file

@ -10,9 +10,7 @@ type
BadClientError* = object of CatchableError
TimelineKind* {.pure.} = enum
tweets
replies
media
tweets, replies, media
Api* {.pure.} = enum
tweetDetail
@ -63,6 +61,12 @@ type
tweetUnavailable = 421
tweetCensored = 422
VerifiedType* = enum
none = "None"
blue = "Blue"
business = "Business"
government = "Government"
User* = object
id*: string
username*: string
@ -78,7 +82,7 @@ type
tweets*: int
likes*: int
media*: int
verified*: bool
verifiedType*: VerifiedType
protected*: bool
suspended*: bool
joinDate*: DateTime

View file

@ -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=18")
link(rel="stylesheet", type="text/css", href="/css/style.css?v=19")
link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=2")
if theme.len > 0:

View file

@ -23,6 +23,13 @@ proc icon*(icon: string; text=""; title=""; class=""; href=""): VNode =
if text.len > 0:
text " " & text
template verifiedIcon*(user: User): untyped {.dirty.} =
if user.verifiedType != none:
let lower = ($user.verifiedType).toLowerAscii()
icon "ok", class=(&"verified-icon {lower}"), title=(&"Verified {lower} account")
else:
text ""
proc linkUser*(user: User, class=""): VNode =
let
isName = "username" notin class
@ -32,11 +39,11 @@ proc linkUser*(user: User, class=""): VNode =
buildHtml(a(href=href, class=class, title=nameText)):
text nameText
if isName and user.verified:
icon "ok", class="verified-icon", title="Verified account"
if isName and user.protected:
text " "
icon "lock", title="Protected account"
if isName:
verifiedIcon(user)
if user.protected:
text " "
icon "lock", title="Protected account"
proc linkText*(text: string; class=""): VNode =
let url = if "http" notin text: https & text else: text

View file

@ -200,8 +200,7 @@ proc renderAttribution(user: User; prefs: Prefs): VNode =
buildHtml(a(class="attribution", href=("/" & user.username))):
renderMiniAvatar(user, prefs)
strong: text user.fullname
if user.verified:
icon "ok", class="verified-icon", title="Verified account"
verifiedIcon(user)
proc renderMediaTags(tags: seq[User]): VNode =
buildHtml(tdiv(class="media-tag-block")):