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…
	
	Add table
		Add a link
		
	
		Reference in a new issue