mirror of
https://github.com/zedeus/nitter.git
synced 2026-03-05 13:30:19 -05:00
Add bulk script create_sessions_browser.py
This commit is contained in:
@@ -20,73 +20,112 @@ Output:
|
|||||||
{"kind": "cookie", "username": "...", "id": "...", "auth_token": "...", "ct0": "..."}
|
{"kind": "cookie", "username": "...", "id": "...", "auth_token": "...", "ct0": "..."}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import pyotp
|
import json
|
||||||
import nodriver as uc
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import nodriver as uc
|
||||||
|
import pyotp
|
||||||
|
|
||||||
|
|
||||||
async def login_and_get_cookies(username, password, totp_seed=None, headless=False):
|
async def login_and_get_cookies(username, password, totp_seed=None, headless=False):
|
||||||
"""Authenticate with X.com and extract session cookies"""
|
"""Authenticate with X.com and extract session cookies"""
|
||||||
# Note: headless mode may increase detection risk from bot-detection systems
|
# Note: headless mode may increase detection risk from bot-detection systems
|
||||||
browser = await uc.start(headless=headless)
|
browser = await uc.start(headless=headless)
|
||||||
tab = await browser.get('https://x.com/i/flow/login')
|
tab = await browser.get("https://x.com/i/flow/login")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Enter username
|
# Enter username
|
||||||
print('[*] Entering username...', file=sys.stderr)
|
print(f"[*] Entering username {username}...", file=sys.stderr)
|
||||||
username_input = await tab.find('input[autocomplete="username"]', timeout=10)
|
|
||||||
await username_input.send_keys(username + '\n')
|
retry = 0
|
||||||
await asyncio.sleep(1)
|
while retry < 5:
|
||||||
|
username_input = await tab.find(
|
||||||
|
'input[autocomplete="username"]', timeout=10
|
||||||
|
)
|
||||||
|
|
||||||
|
pos = await username_input.get_position()
|
||||||
|
await tab.mouse_move(pos.x, pos.y, steps=50, flash=True)
|
||||||
|
await asyncio.sleep(0.1)
|
||||||
|
|
||||||
|
await username_input.click()
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
await username_input.send_keys(username)
|
||||||
|
await asyncio.sleep(0.2)
|
||||||
|
await username_input.send_keys("\n")
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
|
||||||
|
page_content = await tab.get_content()
|
||||||
|
if "Could not log you in" in page_content:
|
||||||
|
retry += 1
|
||||||
|
wait = retry * 10
|
||||||
|
print(f"Retrying in {wait} seconds...")
|
||||||
|
await asyncio.sleep(wait)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
# Enter password
|
# Enter password
|
||||||
print('[*] Entering password...', file=sys.stderr)
|
print("[*] Entering password...", file=sys.stderr)
|
||||||
password_input = await tab.find('input[autocomplete="current-password"]', timeout=15)
|
pretry = 0
|
||||||
await password_input.send_keys(password + '\n')
|
while pretry < 5:
|
||||||
await asyncio.sleep(2)
|
password_input = await tab.find(
|
||||||
|
'input[autocomplete="current-password"]', timeout=15
|
||||||
|
)
|
||||||
|
await password_input.click()
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
await password_input.send_keys(password)
|
||||||
|
await asyncio.sleep(0.2)
|
||||||
|
await password_input.send_keys("\n")
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
|
||||||
|
page_content = await tab.get_content()
|
||||||
|
if "Could not log you in" in page_content:
|
||||||
|
pretry += 1
|
||||||
|
wait = pretry * 10
|
||||||
|
print(f"Retrying in {wait} seconds...")
|
||||||
|
await asyncio.sleep(wait)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
# Handle 2FA if needed
|
# Handle 2FA if needed
|
||||||
page_content = await tab.get_content()
|
page_content = await tab.get_content()
|
||||||
if 'verification code' in page_content or 'Enter code' in page_content:
|
if "verification code" in page_content or "Enter code" in page_content:
|
||||||
if not totp_seed:
|
if not totp_seed:
|
||||||
raise Exception('2FA required but no TOTP seed provided')
|
raise Exception("2FA required but no TOTP seed provided")
|
||||||
|
|
||||||
print('[*] 2FA detected, entering code...', file=sys.stderr)
|
print("[*] 2FA detected, entering code...", file=sys.stderr)
|
||||||
totp_code = pyotp.TOTP(totp_seed).now()
|
totp_code = pyotp.TOTP(totp_seed).now()
|
||||||
code_input = await tab.select('input[type="text"]')
|
code_input = await tab.select('input[type="text"]')
|
||||||
await code_input.send_keys(totp_code + '\n')
|
await code_input.send_keys(totp_code + "\n")
|
||||||
await asyncio.sleep(3)
|
await asyncio.sleep(3)
|
||||||
|
|
||||||
# Get cookies
|
# Get cookies
|
||||||
print('[*] Retrieving cookies...', file=sys.stderr)
|
print("[*] Retrieving cookies...", file=sys.stderr)
|
||||||
for _ in range(20): # 20 second timeout
|
for _ in range(20): # 20 second timeout
|
||||||
cookies = await browser.cookies.get_all()
|
cookies = await browser.cookies.get_all()
|
||||||
cookies_dict = {cookie.name: cookie.value for cookie in cookies}
|
cookies_dict = {cookie.name: cookie.value for cookie in cookies}
|
||||||
|
|
||||||
if 'auth_token' in cookies_dict and 'ct0' in cookies_dict:
|
if "auth_token" in cookies_dict and "ct0" in cookies_dict:
|
||||||
print('[*] Found both cookies', file=sys.stderr)
|
|
||||||
|
|
||||||
# Extract ID from twid cookie (may be URL-encoded)
|
# Extract ID from twid cookie (may be URL-encoded)
|
||||||
user_id = None
|
user_id = None
|
||||||
if 'twid' in cookies_dict:
|
if "twid" in cookies_dict:
|
||||||
twid = cookies_dict['twid']
|
twid = cookies_dict["twid"]
|
||||||
# Try to extract the ID from twid (format: u%3D<id> or u=<id>)
|
# Try to extract the ID from twid (format: u%3D<id> or u=<id>)
|
||||||
if 'u%3D' in twid:
|
if "u%3D" in twid:
|
||||||
user_id = twid.split('u%3D')[1].split('&')[0].strip('"')
|
user_id = twid.split("u%3D")[1].split("&")[0].strip('"')
|
||||||
elif 'u=' in twid:
|
elif "u=" in twid:
|
||||||
user_id = twid.split('u=')[1].split('&')[0].strip('"')
|
user_id = twid.split("u=")[1].split("&")[0].strip('"')
|
||||||
|
|
||||||
cookies_dict['username'] = username
|
cookies_dict["username"] = username
|
||||||
if user_id:
|
if user_id:
|
||||||
cookies_dict['id'] = user_id
|
cookies_dict["id"] = user_id
|
||||||
|
|
||||||
return cookies_dict
|
return cookies_dict
|
||||||
|
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
raise Exception('Timeout waiting for cookies')
|
raise Exception("Timeout waiting for cookies")
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
browser.stop()
|
browser.stop()
|
||||||
@@ -94,7 +133,9 @@ async def login_and_get_cookies(username, password, totp_seed=None, headless=Fal
|
|||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
if len(sys.argv) < 3:
|
if len(sys.argv) < 3:
|
||||||
print('Usage: python3 create_session_browser.py username password [totp_seed] [--append file.jsonl] [--headless]')
|
print(
|
||||||
|
"Usage: python3 create_session_browser.py username password [totp_seed] [--append file.jsonl] [--headless]"
|
||||||
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
username = sys.argv[1]
|
username = sys.argv[1]
|
||||||
@@ -107,49 +148,49 @@ async def main():
|
|||||||
i = 3
|
i = 3
|
||||||
while i < len(sys.argv):
|
while i < len(sys.argv):
|
||||||
arg = sys.argv[i]
|
arg = sys.argv[i]
|
||||||
if arg == '--append':
|
if arg == "--append":
|
||||||
if i + 1 < len(sys.argv):
|
if i + 1 < len(sys.argv):
|
||||||
append_file = sys.argv[i + 1]
|
append_file = sys.argv[i + 1]
|
||||||
i += 2 # Skip '--append' and filename
|
i += 2 # Skip '--append' and filename
|
||||||
else:
|
else:
|
||||||
print('[!] Error: --append requires a filename', file=sys.stderr)
|
print("[!] Error: --append requires a filename", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
elif arg == '--headless':
|
elif arg == "--headless":
|
||||||
headless = True
|
headless = True
|
||||||
i += 1
|
i += 1
|
||||||
elif not arg.startswith('--'):
|
elif not arg.startswith("--"):
|
||||||
if totp_seed is None:
|
if totp_seed is None:
|
||||||
totp_seed = arg
|
totp_seed = arg
|
||||||
i += 1
|
i += 1
|
||||||
else:
|
else:
|
||||||
# Unkown args
|
# Unkown args
|
||||||
print(f'[!] Warning: Unknown argument: {arg}', file=sys.stderr)
|
print(f"[!] Warning: Unknown argument: {arg}", file=sys.stderr)
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cookies = await login_and_get_cookies(username, password, totp_seed, headless)
|
cookies = await login_and_get_cookies(username, password, totp_seed, headless)
|
||||||
session = {
|
session = {
|
||||||
'kind': 'cookie',
|
"kind": "cookie",
|
||||||
'username': cookies['username'],
|
"username": cookies["username"],
|
||||||
'id': cookies.get('id'),
|
"id": cookies.get("id"),
|
||||||
'auth_token': cookies['auth_token'],
|
"auth_token": cookies["auth_token"],
|
||||||
'ct0': cookies['ct0']
|
"ct0": cookies["ct0"],
|
||||||
}
|
}
|
||||||
output = json.dumps(session)
|
output = json.dumps(session)
|
||||||
|
|
||||||
if append_file:
|
if append_file:
|
||||||
with open(append_file, 'a') as f:
|
with open(append_file, "a") as f:
|
||||||
f.write(output + '\n')
|
f.write(output + "\n")
|
||||||
print(f'✓ Session appended to {append_file}', file=sys.stderr)
|
print(f"✓ Session appended to {append_file}", file=sys.stderr)
|
||||||
else:
|
else:
|
||||||
print(output)
|
print(output)
|
||||||
|
|
||||||
os._exit(0)
|
os._exit(0)
|
||||||
|
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
print(f'[!] Error: {error}', file=sys.stderr)
|
print(f"[!] Error: {error}", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|||||||
219
tools/create_sessions_browser.py
Normal file
219
tools/create_sessions_browser.py
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Requirements:
|
||||||
|
pip install -r tools/requirements.txt
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 tools/create_sessions_browser.py <accounts_file> [--append sessions.jsonl] [--headless] [--delay]
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
# Output to terminal
|
||||||
|
python3 tools/create_sessions_browser.py <accounts_file>
|
||||||
|
|
||||||
|
# Append to sessions.jsonl
|
||||||
|
python3 tools/create_sessions_browser.py <accounts_file> --append sessions.jsonl
|
||||||
|
|
||||||
|
# Add 5 second delay between sessions (default: 1)
|
||||||
|
python3 tools/create_sessions_browser.py <accounts_file> --delay 5
|
||||||
|
|
||||||
|
# Headless mode (may increase detection risk)
|
||||||
|
python3 tools/create_sessions_browser.py <accounts_file> --headless
|
||||||
|
|
||||||
|
Input (accounts_file):
|
||||||
|
[{"username": "user", "password": "pass", "totp": "totp_code"}, {...}, ...]
|
||||||
|
|
||||||
|
Output:
|
||||||
|
{"kind": "cookie", "username": "...", "id": "...", "auth_token": "...", "ct0": "..."}
|
||||||
|
{"kind": "cookie", "username": "...", "id": "...", "auth_token": "...", "ct0": "..."}
|
||||||
|
...
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
import nodriver as uc
|
||||||
|
import pyotp
|
||||||
|
|
||||||
|
|
||||||
|
async def login_and_get_cookies(account, headless=False):
|
||||||
|
"""Authenticate with X.com and extract session cookies"""
|
||||||
|
# Note: headless mode may increase detection risk from bot-detection systems
|
||||||
|
browser = await uc.start(headless=headless)
|
||||||
|
tab = await browser.get("https://x.com/i/flow/login")
|
||||||
|
|
||||||
|
username = account["username"]
|
||||||
|
password = account["password"]
|
||||||
|
totp_seed = account["totp"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Enter username
|
||||||
|
print(f"[*] Entering username {username}...", file=sys.stderr)
|
||||||
|
|
||||||
|
retry = 0
|
||||||
|
while retry < 5:
|
||||||
|
username_input = await tab.find(
|
||||||
|
'input[autocomplete="username"]', timeout=10
|
||||||
|
)
|
||||||
|
|
||||||
|
pos = await username_input.get_position()
|
||||||
|
await tab.mouse_move(pos.x, pos.y, steps=50, flash=True)
|
||||||
|
await asyncio.sleep(0.1)
|
||||||
|
|
||||||
|
await username_input.click()
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
await username_input.send_keys(username)
|
||||||
|
await asyncio.sleep(0.2)
|
||||||
|
await username_input.send_keys("\n")
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
|
||||||
|
page_content = await tab.get_content()
|
||||||
|
if "Could not log you in" in page_content:
|
||||||
|
retry += 1
|
||||||
|
wait = retry * 10
|
||||||
|
print(f"Retrying in {wait} seconds...")
|
||||||
|
await asyncio.sleep(wait)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Enter password
|
||||||
|
print("[*] Entering password...", file=sys.stderr)
|
||||||
|
pretry = 0
|
||||||
|
while pretry < 5:
|
||||||
|
password_input = await tab.find(
|
||||||
|
'input[autocomplete="current-password"]', timeout=15
|
||||||
|
)
|
||||||
|
await password_input.click()
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
await password_input.send_keys(password)
|
||||||
|
await asyncio.sleep(0.2)
|
||||||
|
await password_input.send_keys("\n")
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
|
||||||
|
page_content = await tab.get_content()
|
||||||
|
if "Could not log you in" in page_content:
|
||||||
|
pretry += 1
|
||||||
|
wait = pretry * 10
|
||||||
|
print(f"Retrying in {wait} seconds...")
|
||||||
|
await asyncio.sleep(wait)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Handle 2FA if needed
|
||||||
|
page_content = await tab.get_content()
|
||||||
|
if "verification code" in page_content or "Enter code" in page_content:
|
||||||
|
if not totp_seed:
|
||||||
|
raise Exception("2FA required but no TOTP seed provided")
|
||||||
|
|
||||||
|
print("[*] 2FA detected, entering code...", file=sys.stderr)
|
||||||
|
totp_code = pyotp.TOTP(totp_seed).now()
|
||||||
|
code_input = await tab.select('input[type="text"]')
|
||||||
|
await code_input.send_keys(totp_code + "\n")
|
||||||
|
await asyncio.sleep(3)
|
||||||
|
|
||||||
|
# Get cookies
|
||||||
|
print("[*] Retrieving cookies...", file=sys.stderr)
|
||||||
|
for _ in range(20): # 20 second timeout
|
||||||
|
cookies = await browser.cookies.get_all()
|
||||||
|
cookies_dict = {cookie.name: cookie.value for cookie in cookies}
|
||||||
|
|
||||||
|
if "auth_token" in cookies_dict and "ct0" in cookies_dict:
|
||||||
|
# Extract ID from twid cookie (may be URL-encoded)
|
||||||
|
user_id = None
|
||||||
|
if "twid" in cookies_dict:
|
||||||
|
twid = cookies_dict["twid"]
|
||||||
|
# Try to extract the ID from twid (format: u%3D<id> or u=<id>)
|
||||||
|
if "u%3D" in twid:
|
||||||
|
user_id = twid.split("u%3D")[1].split("&")[0].strip('"')
|
||||||
|
elif "u=" in twid:
|
||||||
|
user_id = twid.split("u=")[1].split("&")[0].strip('"')
|
||||||
|
|
||||||
|
cookies_dict["username"] = username
|
||||||
|
if user_id:
|
||||||
|
cookies_dict["id"] = user_id
|
||||||
|
|
||||||
|
return cookies_dict
|
||||||
|
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
|
raise Exception("Timeout waiting for cookies")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
browser.stop()
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print(
|
||||||
|
"Usage: python3 create_sessions_browser.py <accounts_file> [--append sessions.jsonl] [--headless]"
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
input = sys.argv[1]
|
||||||
|
append_file = None
|
||||||
|
headless = False
|
||||||
|
delay = 1
|
||||||
|
|
||||||
|
# Parse optional arguments
|
||||||
|
i = 2
|
||||||
|
while i < len(sys.argv):
|
||||||
|
arg = sys.argv[i]
|
||||||
|
if arg == "--append":
|
||||||
|
if i + 1 < len(sys.argv):
|
||||||
|
append_file = sys.argv[i + 1]
|
||||||
|
i += 2 # Skip '--append' and filename
|
||||||
|
else:
|
||||||
|
print("[!] Error: --append requires a filename", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
elif arg == "--headless":
|
||||||
|
headless = True
|
||||||
|
i += 1
|
||||||
|
elif arg == "--delay":
|
||||||
|
delay = int(sys.argv[i + 1])
|
||||||
|
i += 2
|
||||||
|
else:
|
||||||
|
# Unkown args
|
||||||
|
print(f"[!] Warning: Unknown argument: {arg}", file=sys.stderr)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
accounts = []
|
||||||
|
with open(input) as f:
|
||||||
|
accounts = json.load(f)
|
||||||
|
|
||||||
|
if len(accounts) == 0:
|
||||||
|
print("no accounts in file")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
sessions = 0
|
||||||
|
for acc in accounts:
|
||||||
|
sessions += 1
|
||||||
|
try:
|
||||||
|
cookies = await login_and_get_cookies(acc, headless)
|
||||||
|
session = {
|
||||||
|
"kind": "cookie",
|
||||||
|
"username": cookies["username"],
|
||||||
|
"id": cookies.get("id"),
|
||||||
|
"auth_token": cookies["auth_token"],
|
||||||
|
"ct0": cookies["ct0"],
|
||||||
|
}
|
||||||
|
|
||||||
|
if append_file:
|
||||||
|
with open(append_file, "a") as f:
|
||||||
|
f.write(json.dumps(session) + "\n")
|
||||||
|
else:
|
||||||
|
print(json.dumps(session))
|
||||||
|
|
||||||
|
print(f"Progress: {sessions} / {len(accounts)}")
|
||||||
|
if sessions < len(accounts):
|
||||||
|
print("Waiting", delay, "seconds")
|
||||||
|
sleep(delay)
|
||||||
|
except Exception as error:
|
||||||
|
print(
|
||||||
|
f"[!] Error getting session for {acc["username"]}, skipping: {error}",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
Reference in New Issue
Block a user