Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
S
shadowban-eu-backend
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nanahira
shadowban-eu-backend
Commits
050d3043
Commit
050d3043
authored
Aug 24, 2019
by
root
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
guest session recycling
parent
e01f31c2
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
77 additions
and
47 deletions
+77
-47
backend.py
backend.py
+77
-47
No files found.
backend.py
View file @
050d3043
...
@@ -13,6 +13,8 @@ from aiohttp import web
...
@@ -13,6 +13,8 @@ from aiohttp import web
from
bs4
import
BeautifulSoup
from
bs4
import
BeautifulSoup
from
db
import
connect
from
db
import
connect
twitter_auth_key
=
"AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs
%3
D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA"
routes
=
web
.
RouteTableDef
()
routes
=
web
.
RouteTableDef
()
def
get_nested
(
obj
,
path
,
default
=
None
):
def
get_nested
(
obj
,
path
,
default
=
None
):
...
@@ -22,13 +24,16 @@ def get_nested(obj, path, default=None):
...
@@ -22,13 +24,16 @@ def get_nested(obj, path, default=None):
obj
=
obj
[
p
]
obj
=
obj
[
p
]
return
obj
return
obj
def
is_error
(
result
,
code
):
def
is_error
(
result
,
code
=
None
):
return
isinstance
(
result
.
get
(
"errors"
,
None
),
list
)
and
len
([
x
for
x
in
result
[
"errors"
]
if
x
.
get
(
"code"
,
None
)
==
code
])
>
0
return
isinstance
(
result
.
get
(
"errors"
,
None
),
list
)
and
(
len
([
x
for
x
in
result
[
"errors"
]
if
x
.
get
(
"code"
,
None
)
==
code
])
>
0
or
code
is
None
and
len
(
result
[
"errors"
]
>
0
))
account_sessions
=
[]
account_sessions
=
[]
account_index
=
0
account_index
=
0
log_file
=
None
log_file
=
None
debug_file
=
None
debug_file
=
None
guest_session_pool_size
=
10
guest_sessions
=
[]
test_index
=
0
def
next_session
():
def
next_session
():
sessions
=
sorted
([
s
for
s
in
account_sessions
if
not
s
.
locked
],
key
=
lambda
s
:
-
s
.
remaining
)
sessions
=
sorted
([
s
for
s
in
account_sessions
if
not
s
.
locked
],
key
=
lambda
s
:
-
s
.
remaining
)
...
@@ -36,7 +41,6 @@ def next_session():
...
@@ -36,7 +41,6 @@ def next_session():
return
sessions
[
0
]
return
sessions
[
0
]
class
TwitterSession
:
class
TwitterSession
:
_auth
=
"AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs
%3
D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA"
_user_url
=
"https://api.twitter.com/graphql/SEn6Mq-OakvVOT1CJqUO2A/UserByScreenName?variables="
_user_url
=
"https://api.twitter.com/graphql/SEn6Mq-OakvVOT1CJqUO2A/UserByScreenName?variables="
def
__init__
(
self
):
def
__init__
(
self
):
self
.
_headers
=
{
self
.
_headers
=
{
...
@@ -66,6 +70,26 @@ class TwitterSession:
...
@@ -66,6 +70,26 @@ class TwitterSession:
if
cookie
.
key
==
'ct0'
:
if
cookie
.
key
==
'ct0'
:
self
.
_headers
[
'X-Csrf-Token'
]
=
cookie
.
value
self
.
_headers
[
'X-Csrf-Token'
]
=
cookie
.
value
async
def
get_guest_token
(
self
):
self
.
_headers
[
'Authorization'
]
=
'Bearer '
+
twitter_auth_key
async
with
self
.
_session
.
post
(
"https://api.twitter.com/1.1/guest/activate.json"
,
headers
=
self
.
_headers
)
as
r
:
response
=
await
r
.
json
()
guest_token
=
response
.
get
(
"guest_token"
,
None
)
if
guest_token
is
None
:
debug
(
"Failed to fetch guest token"
)
return
guest_token
async
def
login_guest
(
self
):
if
self
.
_session
is
not
None
:
await
self
.
_session
.
close
()
self
.
_session
=
aiohttp
.
ClientSession
()
self
.
set_csrf_header
()
old_token
=
self
.
_guest_token
new_token
=
await
self
.
get_guest_token
()
self
.
_guest_token
=
new_token
if
new_token
is
not
None
else
old_token
self
.
_headers
[
'X-Guest-Token'
]
=
self
.
_guest_token
async
def
login
(
self
,
username
=
None
,
password
=
None
,
email
=
None
,
cookie_dir
=
None
):
async
def
login
(
self
,
username
=
None
,
password
=
None
,
email
=
None
,
cookie_dir
=
None
):
self
.
_session
=
aiohttp
.
ClientSession
()
self
.
_session
=
aiohttp
.
ClientSession
()
...
@@ -101,62 +125,57 @@ class TwitterSession:
...
@@ -101,62 +125,57 @@ class TwitterSession:
else
:
else
:
async
with
self
.
_session
.
get
(
'https://twitter.com'
,
headers
=
self
.
_headers
)
as
r
:
async
with
self
.
_session
.
get
(
'https://twitter.com'
,
headers
=
self
.
_headers
)
as
r
:
await
r
.
text
()
await
r
.
text
()
self
.
set_csrf_header
()
self
.
set_csrf_header
()
self
.
username
=
username
self
.
username
=
username
if
cookie_file
is
not
None
and
store_cookies
:
if
cookie_file
is
not
None
and
store_cookies
:
self
.
_session
.
cookie_jar
.
save
(
cookie_file
)
self
.
_session
.
cookie_jar
.
save
(
cookie_file
)
else
:
else
:
self
.
_headers
[
'Authorization'
]
=
'Bearer '
+
self
.
_auth
await
self
.
login_guest
()
async
with
self
.
_session
.
post
(
"https://api.twitter.com/1.1/guest/activate.json"
,
headers
=
self
.
_headers
)
as
r
:
guest_token
=
await
r
.
json
()
self
.
_guest_token
=
guest_token
[
"guest_token"
]
self
.
_headers
[
'X-Guest-Token'
]
=
self
.
_guest_token
self
.
_headers
[
'Authorization'
]
=
'Bearer '
+
self
.
_auth
self
.
_headers
[
'Authorization'
]
=
'Bearer '
+
twitter_auth_key
async
def
get
(
self
,
url
,
retries
=
0
):
self
.
set_csrf_header
()
try
:
async
with
self
.
_session
.
get
(
url
,
headers
=
self
.
_headers
)
as
r
:
result
=
await
r
.
json
()
self
.
monitor_rate_limit
(
r
.
headers
)
if
self
.
username
is
None
and
self
.
remaining
<
10
or
is_error
(
result
,
88
):
await
self
.
login_guest
()
if
retries
>
0
and
is_error
(
result
,
353
):
return
await
self
.
get
(
url
,
retries
-
1
)
if
is_error
(
result
,
326
):
self
.
locked
=
True
return
result
except
DisconnectedError
:
if
self
.
username
is
None
:
self
.
login_guest
()
async
def
search_raw
(
self
,
query
,
live
=
True
):
async
def
search_raw
(
self
,
query
,
live
=
True
):
additional_query
=
""
additional_query
=
""
if
live
:
if
live
:
additional_query
=
"&tweet_search_mode=live"
additional_query
=
"&tweet_search_mode=live"
async
with
self
.
_session
.
get
(
"https://api.twitter.com/2/search/adaptive.json?include_profile_interstitial_type=1&include_blocking=1&include_blocked_by=1&include_followed_by=1&include_want_retweets=1&include_mute_edge=1&include_can_dm=1&include_can_media_tag=1&skip_status=1&cards_platform=Web-12&include_cards=1&include_composer_source=true&include_ext_alt_text=true&include_reply_count=1&tweet_mode=extended&include_entities=true&include_user_entities=true&include_ext_media_color=true&include_ext_media_availability=true&send_error_codes=true&q="
+
urllib
.
parse
.
quote
(
query
)
+
"&qf_abuse=false&count=20&query_source=typed_query&pc=1&spelling_corrections=0&ext=mediaStats
%2
ChighlightedLabel
%2
CcameraMoment"
+
additional_query
,
headers
=
self
.
_headers
)
as
r
:
return
await
self
.
get
(
"https://api.twitter.com/2/search/adaptive.json?q="
+
urllib
.
parse
.
quote
(
query
)
+
"&count=20&spelling_corrections=0"
+
additional_query
)
return
await
r
.
json
()
async
def
typeahead_raw
(
self
,
query
):
async
def
typeahead_raw
(
self
,
query
):
async
with
self
.
_session
.
get
(
"https://api.twitter.com/1.1/search/typeahead.json?src=search_box&result_type=users&q="
+
urllib
.
parse
.
quote
(
query
),
headers
=
self
.
_headers
)
as
r
:
return
await
self
.
get
(
"https://api.twitter.com/1.1/search/typeahead.json?src=search_box&result_type=users&q="
+
urllib
.
parse
.
quote
(
query
))
return
await
r
.
json
()
async
def
profile_raw
(
self
,
username
):
async
def
profile_raw
(
self
,
username
):
obj
=
json
.
dumps
({
"screen_name"
:
username
,
"withHighlightedLabel"
:
True
})
obj
=
json
.
dumps
({
"screen_name"
:
username
,
"withHighlightedLabel"
:
True
})
async
with
self
.
_session
.
get
(
self
.
_user_url
+
urllib
.
parse
.
quote
(
obj
),
headers
=
self
.
_headers
)
as
r
:
return
await
self
.
get
(
self
.
_user_url
+
urllib
.
parse
.
quote
(
obj
))
return
await
r
.
json
()
async
def
get_profile_tweets_raw
(
self
,
user_id
):
async
def
get_profile_tweets_raw
(
self
,
user_id
):
async
with
self
.
_session
.
get
(
"https://api.twitter.com/2/timeline/profile/"
+
str
(
user_id
)
+
".json?include_tweet_replies=1&include_want_retweets=0&include_reply_count=1&count=1000"
,
headers
=
self
.
_headers
)
as
r
:
return
await
self
.
get
(
"https://api.twitter.com/2/timeline/profile/"
+
str
(
user_id
)
+
".json?include_tweet_replies=1&include_want_retweets=0&include_reply_count=1&count=1000"
)
return
await
r
.
json
()
async
def
test_detached_tweets
():
pass
async
def
tweet_raw
(
self
,
tweet_id
,
count
=
20
,
cursor
=
None
,
retry_csrf
=
True
):
async
def
tweet_raw
(
self
,
tweet_id
,
count
=
20
,
cursor
=
None
,
retry_csrf
=
True
):
if
cursor
is
None
:
if
cursor
is
None
:
cursor
=
""
cursor
=
""
else
:
else
:
cursor
=
"&cursor="
+
urllib
.
parse
.
quote
(
cursor
)
cursor
=
"&cursor="
+
urllib
.
parse
.
quote
(
cursor
)
async
with
self
.
_session
.
get
(
"https://api.twitter.com/2/timeline/conversation/"
+
tweet_id
+
".json?include_reply_count=1&send_error_codes=true&count="
+
str
(
count
)
+
cursor
,
headers
=
self
.
_headers
)
as
r
:
return
await
self
.
get
(
"https://api.twitter.com/2/timeline/conversation/"
+
tweet_id
+
".json?include_reply_count=1&send_error_codes=true&count="
+
str
(
count
)
+
cursor
)
result
=
await
r
.
json
()
debug
(
'Tweet request '
+
tweet_id
+
':
\n
'
+
str
(
r
)
+
'
\n\n
'
+
json
.
dumps
(
result
)
+
'
\n\n\n
'
)
self
.
set_csrf_header
()
if
self
.
username
is
not
None
:
self
.
monitor_rate_limit
(
r
.
headers
)
if
retry_csrf
and
is_error
(
result
,
353
):
return
await
self
.
tweet_raw
(
tweet_id
,
count
,
cursor
,
False
)
if
is_error
(
result
,
326
):
self
.
locked
=
True
return
result
def
monitor_rate_limit
(
self
,
headers
):
def
monitor_rate_limit
(
self
,
headers
):
# store last remaining count for reset detection
# store last remaining count for reset detection
...
@@ -167,14 +186,14 @@ class TwitterSession:
...
@@ -167,14 +186,14 @@ class TwitterSession:
self
.
reset
=
int
(
headers
.
get
(
'x-rate-limit-reset'
,
-
1
))
self
.
reset
=
int
(
headers
.
get
(
'x-rate-limit-reset'
,
-
1
))
# rate limit reset
# rate limit reset
if
last_remaining
<
self
.
remaining
and
self
.
overshot
>
0
:
if
last_remaining
<
self
.
remaining
and
self
.
overshot
>
0
and
self
.
username
is
not
None
:
log
(
'[rate-limit] Reset detected for '
+
self
.
username
+
'. Saving overshoot count...'
)
log
(
'[rate-limit] Reset detected for '
+
self
.
username
+
'. Saving overshoot count...'
)
db
.
write_rate_limit
({
'screen_name'
:
self
.
username
,
'overshot'
:
self
.
overshot
})
db
.
write_rate_limit
({
'screen_name'
:
self
.
username
,
'overshot'
:
self
.
overshot
})
self
.
overshot
=
0
self
.
overshot
=
0
# count the requests that failed because of rate limiting
# count the requests that failed because of rate limiting
if
self
.
remaining
is
0
:
if
self
.
remaining
is
0
:
log
(
'[rate-limit] Limit hit by '
+
s
elf
.
username
+
'.'
)
log
(
'[rate-limit] Limit hit by '
+
s
tr
(
self
.
username
)
+
'.'
)
self
.
overshot
+=
1
self
.
overshot
+=
1
@
classmethod
@
classmethod
...
@@ -266,12 +285,11 @@ class TwitterSession:
...
@@ -266,12 +285,11 @@ class TwitterSession:
debug
(
'Found:'
+
tid
+
'
\n
'
)
debug
(
'Found:'
+
tid
+
'
\n
'
)
debug
(
'In reply to:'
+
replied_to_id
+
'
\n
'
)
debug
(
'In reply to:'
+
replied_to_id
+
'
\n
'
)
global
account_sessions
global
account_index
reference_session
=
next_session
()
reference_session
=
next_session
()
if
reference_session
is
None
:
if
reference_session
is
None
:
return
return
global
account_index
account_index
+=
1
account_index
+=
1
before_barrier
=
await
reference_session
.
tweet_raw
(
replied_to_id
,
1000
)
before_barrier
=
await
reference_session
.
tweet_raw
(
replied_to_id
,
1000
)
...
@@ -311,7 +329,6 @@ class TwitterSession:
...
@@ -311,7 +329,6 @@ class TwitterSession:
debug
(
traceback
.
format_exc
())
debug
(
traceback
.
format_exc
())
async
def
test
(
self
,
username
,
more_replies_test
=
True
):
async
def
test
(
self
,
username
,
more_replies_test
=
True
):
await
self
.
login
()
result
=
{
"timestamp"
:
time
.
time
()}
result
=
{
"timestamp"
:
time
.
time
()}
profile
=
{}
profile
=
{}
profile_raw
=
await
self
.
profile_raw
(
username
)
profile_raw
=
await
self
.
profile_raw
(
username
)
...
@@ -383,7 +400,6 @@ class TwitterSession:
...
@@ -383,7 +400,6 @@ class TwitterSession:
result
[
"tests"
][
"more_replies"
]
=
await
self
.
test_barrier
(
user_id
)
result
[
"tests"
][
"more_replies"
]
=
await
self
.
test_barrier
(
user_id
)
debug
(
'Writing result for '
+
result
[
'profile'
][
'screen_name'
]
+
' to DB'
);
debug
(
'Writing result for '
+
result
[
'profile'
][
'screen_name'
]
+
' to DB'
);
global
db
db
.
write_result
(
result
)
db
.
write_result
(
result
)
return
result
return
result
...
@@ -392,7 +408,6 @@ class TwitterSession:
...
@@ -392,7 +408,6 @@ class TwitterSession:
await
self
.
_session
.
close
()
await
self
.
_session
.
close
()
def
debug
(
message
):
def
debug
(
message
):
global
debug_file
if
message
.
endswith
(
'
\n
'
)
is
False
:
if
message
.
endswith
(
'
\n
'
)
is
False
:
message
=
message
+
'
\n
'
message
=
message
+
'
\n
'
...
@@ -403,7 +418,6 @@ def debug(message):
...
@@ -403,7 +418,6 @@ def debug(message):
print
(
message
)
print
(
message
)
def
log
(
message
):
def
log
(
message
):
global
log_file
# ensure newline
# ensure newline
if
message
.
endswith
(
'
\n
'
)
is
False
:
if
message
.
endswith
(
'
\n
'
)
is
False
:
message
=
message
+
'
\n
'
message
=
message
+
'
\n
'
...
@@ -414,11 +428,18 @@ def log(message):
...
@@ -414,11 +428,18 @@ def log(message):
else
:
else
:
print
(
message
)
print
(
message
)
def
print_session_info
(
sessions
):
text
=
""
for
session
in
sessions
:
text
+=
"
\n
%6
d
%5
d
%9
d
%5
d"
%
(
int
(
session
.
locked
),
session
.
limit
,
session
.
remaining
,
session
.
reset
-
int
(
time
.
time
()))
return
text
@
routes
.
get
(
'/.stats'
)
@
routes
.
get
(
'/.stats'
)
async
def
stats
(
request
):
async
def
stats
(
request
):
text
=
"Locked Limit Remaining Reset"
text
=
"--- GUEST SESSIONS ---
\n\n
Locked Limit Remaining Reset"
for
session
in
account_sessions
:
text
+=
print_session_info
(
guest_sessions
)
text
+=
"
\n
%6
d
%5
d
%9
d
%5
d"
%
(
int
(
session
.
locked
),
session
.
limit
,
session
.
remaining
,
session
.
reset
-
int
(
time
.
time
()))
text
+=
"
\n\n\n
--- ACCOUNTS ---
\n\n
Locked Limit Remaining Reset"
text
+=
print_session_info
(
account_sessions
)
return
web
.
Response
(
text
=
text
)
return
web
.
Response
(
text
=
text
)
@
routes
.
get
(
'/.unlocked/{screen_name}'
)
@
routes
.
get
(
'/.unlocked/{screen_name}'
)
...
@@ -435,11 +456,12 @@ async def unlocked(request):
...
@@ -435,11 +456,12 @@ async def unlocked(request):
@
routes
.
get
(
'/{screen_name}'
)
@
routes
.
get
(
'/{screen_name}'
)
async
def
api
(
request
):
async
def
api
(
request
):
global
test_index
screen_name
=
request
.
match_info
[
'screen_name'
]
screen_name
=
request
.
match_info
[
'screen_name'
]
session
=
TwitterSession
()
session
=
guest_sessions
[
test_index
%
len
(
guest_sessions
)]
test_index
+=
1
result
=
await
session
.
test
(
screen_name
)
result
=
await
session
.
test
(
screen_name
)
log
(
json
.
dumps
(
result
)
+
'
\n
'
)
log
(
json
.
dumps
(
result
)
+
'
\n
'
)
await
session
.
close
()
return
web
.
json_response
(
result
)
return
web
.
json_response
(
result
)
async
def
login_accounts
(
accounts
,
cookie_dir
=
None
):
async
def
login_accounts
(
accounts
,
cookie_dir
=
None
):
...
@@ -452,6 +474,13 @@ async def login_accounts(accounts, cookie_dir=None):
...
@@ -452,6 +474,13 @@ async def login_accounts(accounts, cookie_dir=None):
account_sessions
.
append
(
session
)
account_sessions
.
append
(
session
)
await
asyncio
.
gather
(
*
coroutines
)
await
asyncio
.
gather
(
*
coroutines
)
async
def
login_guests
():
for
i
in
range
(
0
,
guest_session_pool_size
):
session
=
TwitterSession
()
guest_sessions
.
append
(
session
)
await
asyncio
.
gather
(
*
[
s
.
login
()
for
s
in
guest_sessions
])
log
(
"Guest sessions created"
)
parser
=
argparse
.
ArgumentParser
(
description
=
'Twitter Shadowban Tester'
)
parser
=
argparse
.
ArgumentParser
(
description
=
'Twitter Shadowban Tester'
)
parser
.
add_argument
(
'--account-file'
,
type
=
str
,
default
=
'.htaccounts'
,
help
=
'json file with reference account credentials'
)
parser
.
add_argument
(
'--account-file'
,
type
=
str
,
default
=
'.htaccounts'
,
help
=
'json file with reference account credentials'
)
...
@@ -485,6 +514,7 @@ if args.debug is not None:
...
@@ -485,6 +514,7 @@ if args.debug is not None:
def
run
():
def
run
():
loop
=
asyncio
.
get_event_loop
()
loop
=
asyncio
.
get_event_loop
()
loop
.
run_until_complete
(
login_accounts
(
accounts
,
args
.
cookie_dir
))
loop
.
run_until_complete
(
login_accounts
(
accounts
,
args
.
cookie_dir
))
loop
.
run_until_complete
(
login_guests
())
app
=
web
.
Application
()
app
=
web
.
Application
()
app
.
add_routes
(
routes
)
app
.
add_routes
(
routes
)
web
.
run_app
(
app
,
host
=
'127.0.0.1'
,
port
=
args
.
port
)
web
.
run_app
(
app
,
host
=
'127.0.0.1'
,
port
=
args
.
port
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment