Detect suspended accounts
This commit is contained in:
parent
240ce15651
commit
8a6978cf74
8 changed files with 38 additions and 6 deletions
|
@ -1,4 +1,4 @@
|
|||
import asyncdispatch, strutils, uri
|
||||
import asyncdispatch, strutils, uri, httpclient, json, xmltree, htmlparser
|
||||
|
||||
import ".."/[types, parser]
|
||||
import utils, consts, media
|
||||
|
@ -7,11 +7,21 @@ proc getTweet*(username, id, after, agent: string): Future[Conversation] {.async
|
|||
let
|
||||
headers = genHeaders({
|
||||
"pragma": "no-cache",
|
||||
"x-previous-page-name": "profile"
|
||||
"x-previous-page-name": "profile",
|
||||
"accept": htmlAccept
|
||||
}, agent, base, xml=true)
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ withDb:
|
|||
except DbError: discard
|
||||
|
||||
safeAddColumn Profile.lowername
|
||||
safeAddColumn Profile.suspended
|
||||
|
||||
var profileCacheTime = initDuration(minutes=10)
|
||||
|
||||
|
|
|
@ -127,3 +127,6 @@ proc getLocation*(u: Profile | Tweet): (string, string) =
|
|||
let loc = u.location.split(":")
|
||||
let url = if loc.len > 1: "/search?q=place:" & loc[1] else: ""
|
||||
(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
|
||||
|
||||
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 =
|
||||
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 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"
|
||||
)
|
||||
|
||||
if profile.suspended:
|
||||
return (profile.username, "suspended")
|
||||
|
||||
if timeline != nil:
|
||||
let rss = renderTimelineRss(timeline, profile, hostname, multi=(names.len > 1))
|
||||
return (rss, timeline.minId)
|
||||
|
@ -36,6 +39,8 @@ proc showRss*(req: Request; hostname: string; query: Query): Future[(string, str
|
|||
template respRss*(rss, minId) =
|
||||
if rss.len == 0:
|
||||
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}
|
||||
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)
|
||||
r = await rail
|
||||
if p.username.len == 0: return
|
||||
if p.suspended:
|
||||
return showError(getSuspended(p.username), cfg)
|
||||
|
||||
let pHtml = renderProfile(p, t, r, prefs, getPath())
|
||||
return renderMain(pHtml, request, cfg, pageTitle(p), pageDesc(p),
|
||||
rss=rss, images = @[p.getUserpic("_200x200")])
|
||||
|
|
|
@ -25,6 +25,7 @@ dbTypes:
|
|||
media*: string
|
||||
verified*: bool
|
||||
protected*: bool
|
||||
suspended*: bool
|
||||
joinDate* {.
|
||||
dbType: "INTEGER"
|
||||
parseIt: it.i.fromUnix()
|
||||
|
|
|
@ -71,9 +71,8 @@ class ProfileTest(BaseTestCase):
|
|||
self.assert_text(f'User "{username}" not found')
|
||||
|
||||
def test_suspended(self):
|
||||
# TODO: detect suspended
|
||||
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)
|
||||
def test_banner_color(self, username, color):
|
||||
|
|
Loading…
Reference in a new issue