automation labels
This commit is contained in:
parent
536f9d7fab
commit
f76cfcc154
@ -30,8 +30,6 @@ proc parseUser(js: JsonNode; id=""): User =
|
|||||||
result.expandUserEntities(js)
|
result.expandUserEntities(js)
|
||||||
|
|
||||||
proc parseGraphUser*(js: JsonNode): User =
|
proc parseGraphUser*(js: JsonNode): User =
|
||||||
echo "node: ", $js
|
|
||||||
|
|
||||||
var user = js{"data", "user", "result"}
|
var user = js{"data", "user", "result"}
|
||||||
if user.isNull:
|
if user.isNull:
|
||||||
user = js{"user_results", "result"}
|
user = js{"user_results", "result"}
|
||||||
@ -40,6 +38,16 @@ proc parseGraphUser*(js: JsonNode): User =
|
|||||||
|
|
||||||
result = parseUser(user{"legacy"}, user{"rest_id"}.getStr)
|
result = parseUser(user{"legacy"}, user{"rest_id"}.getStr)
|
||||||
|
|
||||||
|
let label = user{"affiliates_highlighted_label", "label"}
|
||||||
|
if not label.isNull and label{"userLabelType"}.getStr == "AutomatedLabel":
|
||||||
|
result.bot = true
|
||||||
|
let entities = label{"longDescription", "entities"}
|
||||||
|
if not entities.isNull:
|
||||||
|
for ent in entities:
|
||||||
|
if ent{"ref", "type"}.getStr != "TimelineRichTextMention": continue
|
||||||
|
result.botOwner = ent{"ref", "screen_name"}.getStr
|
||||||
|
break
|
||||||
|
|
||||||
if result.verifiedType == VerifiedType.none and user{"is_blue_verified"}.getBool(false):
|
if result.verifiedType == VerifiedType.none and user{"is_blue_verified"}.getBool(false):
|
||||||
result.verifiedType = blue
|
result.verifiedType = blue
|
||||||
|
|
||||||
|
|||||||
@ -128,6 +128,14 @@ proc getTweetById*(id: string; after=""): Future[string] {.async.} =
|
|||||||
params = {"variables": variables, "features": gqlFeatures}
|
params = {"variables": variables, "features": gqlFeatures}
|
||||||
result = await fetchRaw(graphTweet ? params, Api.tweetDetail)
|
result = await fetchRaw(graphTweet ? params, Api.tweetDetail)
|
||||||
|
|
||||||
|
proc getUser*(username: string): Future[string] {.async.} =
|
||||||
|
if username.len == 0: return
|
||||||
|
let
|
||||||
|
variables = """{"screen_name":"$1"}""" % username
|
||||||
|
fieldToggles = """{"withAuxiliaryUserLabels":true}"""
|
||||||
|
params = {"variables": variables, "features": gqlFeatures, "fieldToggles": fieldToggles}
|
||||||
|
result = await fetchRaw(graphUser ? params, Api.userScreenName)
|
||||||
|
|
||||||
proc createTwitterApiRouter*(cfg: Config) =
|
proc createTwitterApiRouter*(cfg: Config) =
|
||||||
router api:
|
router api:
|
||||||
get "/api/echo":
|
get "/api/echo":
|
||||||
@ -135,8 +143,8 @@ proc createTwitterApiRouter*(cfg: Config) =
|
|||||||
|
|
||||||
get "/api/user/@username":
|
get "/api/user/@username":
|
||||||
let username = @"username"
|
let username = @"username"
|
||||||
let response = await getUserProfileJson(username)
|
let response = await getUser(username)
|
||||||
respJson response
|
resp Http200, { "Content-Type": "application/json" }, response
|
||||||
|
|
||||||
#get "/api/user/@id/tweets":
|
#get "/api/user/@id/tweets":
|
||||||
# let id = @"id"
|
# let id = @"id"
|
||||||
|
|||||||
@ -82,7 +82,8 @@
|
|||||||
|
|
||||||
.profile-joindate,
|
.profile-joindate,
|
||||||
.profile-location,
|
.profile-location,
|
||||||
.profile-website {
|
.profile-website,
|
||||||
|
.profile-automated {
|
||||||
color: var(--fg_faded);
|
color: var(--fg_faded);
|
||||||
margin: 1px 0;
|
margin: 1px 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@ -1,242 +1,254 @@
|
|||||||
@import '_variables';
|
@import "_variables";
|
||||||
@import '_mixins';
|
@import "_mixins";
|
||||||
@import 'thread';
|
@import "thread";
|
||||||
@import 'media';
|
@import "media";
|
||||||
@import 'video';
|
@import "video";
|
||||||
@import 'embed';
|
@import "embed";
|
||||||
@import 'card';
|
@import "card";
|
||||||
@import 'poll';
|
@import "poll";
|
||||||
@import 'quote';
|
@import "quote";
|
||||||
@import 'community_note';
|
@import "community_note";
|
||||||
|
|
||||||
.tweet-body {
|
.tweet-body {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
margin-left: 58px;
|
margin-left: 58px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tweet-content {
|
.tweet-content {
|
||||||
font-family: $font_3;
|
font-family: $font_3;
|
||||||
line-height: 1.3em;
|
line-height: 1.3em;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tweet-bidi {
|
.tweet-bidi {
|
||||||
display: block !important;
|
display: block !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tweet-header {
|
.tweet-header {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
flex-basis: 100%;
|
flex-basis: 100%;
|
||||||
margin-bottom: .2em;
|
margin-bottom: 0.2em;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tweet-name-row {
|
.tweet-name-row {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fullname-and-username {
|
.fullname-and-username {
|
||||||
display: flex;
|
display: flex;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fullname {
|
.fullname {
|
||||||
@include ellipsis;
|
@include ellipsis;
|
||||||
flex-shrink: 2;
|
flex-shrink: 2;
|
||||||
max-width: 80%;
|
max-width: 80%;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--fg_color);
|
color: var(--fg_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.username {
|
.username {
|
||||||
@include ellipsis;
|
@include ellipsis;
|
||||||
min-width: 1.6em;
|
min-width: 1.6em;
|
||||||
margin-left: .4em;
|
margin-left: 0.4em;
|
||||||
word-wrap: normal;
|
word-wrap: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-automated {
|
||||||
|
@include ellipsis;
|
||||||
|
min-width: 1px;
|
||||||
|
margin-left: 0.4em;
|
||||||
|
color: var(--fg_faded);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tweet-date {
|
.tweet-date {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tweet-date a, .username, .show-more a {
|
.tweet-date a,
|
||||||
color: var(--fg_dark);
|
.username,
|
||||||
|
.show-more a {
|
||||||
|
color: var(--fg_dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tweet-published {
|
.tweet-published {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
color: var(--grey);
|
color: var(--grey);
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tweet-avatar {
|
.tweet-avatar {
|
||||||
display: contents !important;
|
display: contents !important;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
float: left;
|
float: left;
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
margin-left: -58px;
|
margin-left: -58px;
|
||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
&.round {
|
&.round {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mini {
|
&.mini {
|
||||||
position: unset;
|
position: unset;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
margin-top: -1px;
|
margin-top: -1px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tweet-embed {
|
.tweet-embed {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
background-color: var(--bg_panel);
|
||||||
|
|
||||||
|
.tweet-content {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tweet-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
max-height: calc(100vh - 0.75em * 2);
|
||||||
height: 100%;
|
}
|
||||||
background-color: var(--bg_panel);
|
|
||||||
|
|
||||||
.tweet-content {
|
.card-image img {
|
||||||
font-size: 18px;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tweet-body {
|
.avatar {
|
||||||
display: flex;
|
position: absolute;
|
||||||
flex-direction: column;
|
}
|
||||||
max-height: calc(100vh - 0.75em * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-image img {
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.attribution {
|
.attribution {
|
||||||
display: flex;
|
display: flex;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
|
|
||||||
strong {
|
strong {
|
||||||
color: var(--fg_color);
|
color: var(--fg_color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-tag-block {
|
.media-tag-block {
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
|
color: var(--fg_faded);
|
||||||
|
|
||||||
|
.icon-container {
|
||||||
|
padding-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media-tag,
|
||||||
|
.icon-container {
|
||||||
color: var(--fg_faded);
|
color: var(--fg_faded);
|
||||||
|
}
|
||||||
.icon-container {
|
|
||||||
padding-right: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-tag, .icon-container {
|
|
||||||
color: var(--fg_faded);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.timeline-container .media-tag-block {
|
.timeline-container .media-tag-block {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tweet-geo {
|
.tweet-geo {
|
||||||
color: var(--fg_faded);
|
color: var(--fg_faded);
|
||||||
}
|
}
|
||||||
|
|
||||||
.replying-to {
|
.replying-to {
|
||||||
color: var(--fg_faded);
|
color: var(--fg_faded);
|
||||||
margin: -2px 0 4px;
|
margin: -2px 0 4px;
|
||||||
|
|
||||||
a {
|
a {
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.retweet-header, .pinned, .tweet-stats {
|
.retweet-header,
|
||||||
align-content: center;
|
.pinned,
|
||||||
color: var(--grey);
|
.tweet-stats {
|
||||||
display: flex;
|
align-content: center;
|
||||||
flex-shrink: 0;
|
color: var(--grey);
|
||||||
flex-wrap: wrap;
|
display: flex;
|
||||||
font-size: 14px;
|
flex-shrink: 0;
|
||||||
font-weight: 600;
|
flex-wrap: wrap;
|
||||||
line-height: 22px;
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 22px;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
@include ellipsis;
|
@include ellipsis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.retweet-header {
|
.retweet-header {
|
||||||
margin-top: -5px !important;
|
margin-top: -5px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tweet-stats {
|
.tweet-stats {
|
||||||
margin-bottom: -3px;
|
margin-bottom: -3px;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tweet-stat {
|
.tweet-stat {
|
||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
min-width: 1em;
|
min-width: 1em;
|
||||||
margin-right: 0.8em;
|
margin-right: 0.8em;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.show-thread {
|
.show-thread {
|
||||||
display: block;
|
display: block;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
padding-top: 2px;
|
padding-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.unavailable-box {
|
.unavailable-box {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border: solid 1px var(--dark_grey);
|
border: solid 1px var(--dark_grey);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background-color: var(--bg_color);
|
background-color: var(--bg_color);
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tweet-link {
|
.tweet-link {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--bg_hover);
|
background-color: var(--bg_hover);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -99,6 +99,8 @@ type
|
|||||||
protected*: bool
|
protected*: bool
|
||||||
suspended*: bool
|
suspended*: bool
|
||||||
joinDate*: DateTime
|
joinDate*: DateTime
|
||||||
|
bot*: bool
|
||||||
|
botOwner*: string
|
||||||
|
|
||||||
VideoType* = enum
|
VideoType* = enum
|
||||||
m3u8 = "application/x-mpegURL"
|
m3u8 = "application/x-mpegURL"
|
||||||
|
|||||||
@ -221,13 +221,18 @@ proc getMastoAPIUser*(user: User, cfg: Config): JsonNode =
|
|||||||
website["verified_at"] = newJNull()
|
website["verified_at"] = newJNull()
|
||||||
fields.add(website)
|
fields.add(website)
|
||||||
|
|
||||||
|
if user.botOwner.len > 0:
|
||||||
|
var botOwner = newJObject()
|
||||||
|
botOwner["name"] = %"Automated by"
|
||||||
|
botOwner["value"] = %(&"<a href=\"{getUrlPrefix(cfg)}/{user.botOwner}\" translate=\"no\">{user.botOwner}</a>")
|
||||||
|
|
||||||
var userJson = newJObject()
|
var userJson = newJObject()
|
||||||
userJson["id"] = %user.id
|
userJson["id"] = %user.id
|
||||||
userJson["username"] = %user.username
|
userJson["username"] = %user.username
|
||||||
userJson["acct"] = %user.username
|
userJson["acct"] = %user.username
|
||||||
userJson["display_name"] = %user.fullname
|
userJson["display_name"] = %user.fullname
|
||||||
userJson["locked"] = %user.protected
|
userJson["locked"] = %user.protected
|
||||||
userJson["bot"] = %false # TODO?
|
userJson["bot"] = %user.bot
|
||||||
userJson["discoverable"] = %true
|
userJson["discoverable"] = %true
|
||||||
userJson["indexable"] = %false
|
userJson["indexable"] = %false
|
||||||
userJson["group"] = %false
|
userJson["group"] = %false
|
||||||
|
|||||||
@ -35,6 +35,15 @@ proc renderUserCard*(user: User; prefs: Prefs; path: string): VNode =
|
|||||||
buttonReferer "/unfollow/" & user.username, "Unfollow", path, "profile-card-follow-button"
|
buttonReferer "/unfollow/" & user.username, "Unfollow", path, "profile-card-follow-button"
|
||||||
|
|
||||||
tdiv(class="profile-card-extra"):
|
tdiv(class="profile-card-extra"):
|
||||||
|
if user.bot:
|
||||||
|
tdiv(class="profile-automated"):
|
||||||
|
span:
|
||||||
|
if user.botOwner.len > 0:
|
||||||
|
icon "cog", "Automated by "
|
||||||
|
a(href=(&"/{user.botOwner}")): text &"@{user.botOwner}"
|
||||||
|
else:
|
||||||
|
icon "cog", "Automated"
|
||||||
|
|
||||||
if user.bio.len > 0:
|
if user.bio.len > 0:
|
||||||
tdiv(class="profile-bio"):
|
tdiv(class="profile-bio"):
|
||||||
p(dir="auto"):
|
p(dir="auto"):
|
||||||
|
|||||||
@ -34,6 +34,8 @@ proc renderHeader(tweet: Tweet; retweet: string; pinned: bool; prefs: Prefs): VN
|
|||||||
tdiv(class="fullname-and-username"):
|
tdiv(class="fullname-and-username"):
|
||||||
linkUser(tweet.user, class="fullname")
|
linkUser(tweet.user, class="fullname")
|
||||||
linkUser(tweet.user, class="username")
|
linkUser(tweet.user, class="username")
|
||||||
|
if tweet.user.bot:
|
||||||
|
tdiv(class="user-automated"): icon "cog", "Automated"
|
||||||
|
|
||||||
span(class="tweet-date"):
|
span(class="tweet-date"):
|
||||||
a(href=getLink(tweet), title=tweet.getTime):
|
a(href=getLink(tweet), title=tweet.getTime):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user