Detect suspended accounts
This commit is contained in:
parent
240ce15651
commit
8a6978cf74
|
@ -1,4 +1,4 @@
|
||||||
import asyncdispatch, strutils, uri
|
import asyncdispatch, strutils, uri, httpclient, json, xmltree, htmlparser
|
||||||
|
|
||||||
import ".."/[types, parser]
|
import ".."/[types, parser]
|
||||||
import utils, consts, media
|
import utils, consts, media
|
||||||
|
@ -7,11 +7,21 @@ proc getTweet*(username, id, after, agent: string): Future[Conversation] {.async
|
||||||
let
|
let
|
||||||
headers = genHeaders({
|
headers = genHeaders({
|
||||||
"pragma": "no-cache",
|
"pragma": "no-cache",
|
||||||
"x-previous-page-name": "profile"
|
"x-previous-page-name": "profile",
|
||||||
|
"accept": htmlAccept
|
||||||
}, agent, base, xml=true)
|
}, agent, base, xml=true)
|
||||||
|
|
||||||
url = base / username / tweetUrl / id ? {"max_position": after}
|
url = base / username / tweetUrl / id ? {"max_position": after}
|
||||||
html = await fetchHtml(url, headers)
|
|
||||||
|
newClient()
|
||||||
|
var html: XmlNode
|
||||||
|
try:
|
||||||
|
let resp = await client.get($url)
|
||||||
|
if resp.code == Http403 and "suspended" in (await resp.body):
|
||||||
|
return Conversation(tweet: Tweet(tombstone: "User has been suspended"))
|
||||||
|
html = parseHtml(await resp.body)
|
||||||
|
except:
|
||||||
|
discard
|
||||||
|
|
||||||
if html == nil: return
|
if html == nil: return
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ withDb:
|
||||||
except DbError: discard
|
except DbError: discard
|
||||||
|
|
||||||
safeAddColumn Profile.lowername
|
safeAddColumn Profile.lowername
|
||||||
|
safeAddColumn Profile.suspended
|
||||||
|
|
||||||
var profileCacheTime = initDuration(minutes=10)
|
var profileCacheTime = initDuration(minutes=10)
|
||||||
|
|
||||||
|
|
|
@ -127,3 +127,6 @@ proc getLocation*(u: Profile | Tweet): (string, string) =
|
||||||
let loc = u.location.split(":")
|
let loc = u.location.split(":")
|
||||||
let url = if loc.len > 1: "/search?q=place:" & loc[1] else: ""
|
let url = if loc.len > 1: "/search?q=place:" & loc[1] else: ""
|
||||||
(loc[0], url)
|
(loc[0], url)
|
||||||
|
|
||||||
|
proc getSuspended*(username: string): string =
|
||||||
|
&"User \"{username}\" has been suspended"
|
||||||
|
|
|
@ -2,9 +2,19 @@ import xmltree, sequtils, strutils, json, options
|
||||||
|
|
||||||
import types, parserutils, formatters
|
import types, parserutils, formatters
|
||||||
|
|
||||||
|
proc parseJsonData*(node: XmlNode): JsonNode =
|
||||||
|
let jsonData = node.selectAttr("input.json-data", "value")
|
||||||
|
if jsonData.len > 0:
|
||||||
|
return parseJson(jsonData)
|
||||||
|
|
||||||
proc parseTimelineProfile*(node: XmlNode): Profile =
|
proc parseTimelineProfile*(node: XmlNode): Profile =
|
||||||
let profile = node.select(".ProfileHeaderCard")
|
let profile = node.select(".ProfileHeaderCard")
|
||||||
if profile == nil: return
|
if profile == nil:
|
||||||
|
let data = parseJsonData(node)
|
||||||
|
if data != nil and data{"sectionName"}.getStr == "suspended":
|
||||||
|
let username = data{"internalReferer"}.getStr.strip(chars={'/'})
|
||||||
|
return Profile(username: username, suspended: true)
|
||||||
|
return
|
||||||
|
|
||||||
let pre = ".ProfileHeaderCard-"
|
let pre = ".ProfileHeaderCard-"
|
||||||
let username = profile.getUsername(pre & "screenname")
|
let username = profile.getUsername(pre & "screenname")
|
||||||
|
|
|
@ -29,6 +29,9 @@ proc showRss*(req: Request; hostname: string; query: Query): Future[(string, str
|
||||||
userpic: "https://abs.twimg.com/sticky/default_profile_images/default_profile.png"
|
userpic: "https://abs.twimg.com/sticky/default_profile_images/default_profile.png"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if profile.suspended:
|
||||||
|
return (profile.username, "suspended")
|
||||||
|
|
||||||
if timeline != nil:
|
if timeline != nil:
|
||||||
let rss = renderTimelineRss(timeline, profile, hostname, multi=(names.len > 1))
|
let rss = renderTimelineRss(timeline, profile, hostname, multi=(names.len > 1))
|
||||||
return (rss, timeline.minId)
|
return (rss, timeline.minId)
|
||||||
|
@ -36,6 +39,8 @@ proc showRss*(req: Request; hostname: string; query: Query): Future[(string, str
|
||||||
template respRss*(rss, minId) =
|
template respRss*(rss, minId) =
|
||||||
if rss.len == 0:
|
if rss.len == 0:
|
||||||
resp Http404, showError("User \"" & @"name" & "\" not found", cfg)
|
resp Http404, showError("User \"" & @"name" & "\" not found", cfg)
|
||||||
|
elif minId == "suspended":
|
||||||
|
resp Http404, showError(getSuspended(rss), cfg)
|
||||||
let headers = {"Content-Type": "application/rss+xml;charset=utf-8", "Min-Id": minId}
|
let headers = {"Content-Type": "application/rss+xml;charset=utf-8", "Min-Id": minId}
|
||||||
resp Http200, headers, rss
|
resp Http200, headers, rss
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,9 @@ proc showTimeline*(request: Request; query: Query; cfg: Config; prefs: Prefs;
|
||||||
(p, t) = await fetchSingleTimeline(names[0], after, agent, query)
|
(p, t) = await fetchSingleTimeline(names[0], after, agent, query)
|
||||||
r = await rail
|
r = await rail
|
||||||
if p.username.len == 0: return
|
if p.username.len == 0: return
|
||||||
|
if p.suspended:
|
||||||
|
return showError(getSuspended(p.username), cfg)
|
||||||
|
|
||||||
let pHtml = renderProfile(p, t, r, prefs, getPath())
|
let pHtml = renderProfile(p, t, r, prefs, getPath())
|
||||||
return renderMain(pHtml, request, cfg, pageTitle(p), pageDesc(p),
|
return renderMain(pHtml, request, cfg, pageTitle(p), pageDesc(p),
|
||||||
rss=rss, images = @[p.getUserpic("_200x200")])
|
rss=rss, images = @[p.getUserpic("_200x200")])
|
||||||
|
|
|
@ -25,6 +25,7 @@ dbTypes:
|
||||||
media*: string
|
media*: string
|
||||||
verified*: bool
|
verified*: bool
|
||||||
protected*: bool
|
protected*: bool
|
||||||
|
suspended*: bool
|
||||||
joinDate* {.
|
joinDate* {.
|
||||||
dbType: "INTEGER"
|
dbType: "INTEGER"
|
||||||
parseIt: it.i.fromUnix()
|
parseIt: it.i.fromUnix()
|
||||||
|
|
|
@ -71,9 +71,8 @@ class ProfileTest(BaseTestCase):
|
||||||
self.assert_text(f'User "{username}" not found')
|
self.assert_text(f'User "{username}" not found')
|
||||||
|
|
||||||
def test_suspended(self):
|
def test_suspended(self):
|
||||||
# TODO: detect suspended
|
|
||||||
self.open_nitter('test')
|
self.open_nitter('test')
|
||||||
self.assert_text(f'User "test" not found')
|
self.assert_text(f'User "test" has been suspended')
|
||||||
|
|
||||||
@parameterized.expand(banner_color)
|
@parameterized.expand(banner_color)
|
||||||
def test_banner_color(self, username, color):
|
def test_banner_color(self, username, color):
|
||||||
|
|
Loading…
Reference in New Issue