Skip to content

Commit

Permalink
chore: dev-run.sh
Browse files Browse the repository at this point in the history
  • Loading branch information
kristobalus committed Feb 28, 2024
1 parent 6313623 commit b9d38a3
Show file tree
Hide file tree
Showing 11 changed files with 345 additions and 31 deletions.
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1 +1 @@
NITTER_GUEST_ACCOUNTS_URL=https://guest-keeper.fly.dev/guest-accounts
NITTER_GUEST_ACCOUNTS=[{"oauth_token": "1761830062448357376-fxw4T42gsmuP6DwMHPyX5y0rB4gjCH", "oauth_token_secret": "1M2q8ynnadZKrZjaAb8eeNXnbGRVawwoSGIaYOSrquFmk"},{"oauth_token": "1761829912413872128-9hslQeedW43tG8x5GInmtDogEFpVmF", "oauth_token_secret": "x7tTPr3CsrOoNox9we1LuNmgZAoDrFs1vuRvy9HiIFmJ7"},{"oauth_token": "1761830326618173440-VbeMAkNQAHgw8HI3or1r4dw0DaFZNq", "oauth_token_secret": "IUne5k56CeLrizMTggpqO7cclt922d4SRifQIOnap7KuV"},{"oauth_token": "1761827539700400128-74NfjPLnqF0OgXkPYuWHdyTSCa6NkI", "oauth_token_secret": "OQcrZloaTZeOoyoUaksuxXc1KNE8Nrjrvpn7RmvR7QbFH"},{"oauth_token": "1761829093257994240-dlu8XZKfTWIb9sNaGwZhdmBabPzUOs", "oauth_token_secret": "skHQVBzBIqjCAOkJorQymn5lkgs8x7yUOVHYPz9TwvFnx"}]
2 changes: 1 addition & 1 deletion build-image-fly.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash

# Read the version from package.json
VERSION=1.2.1-fly
VERSION=1.4.0-fly
IMAGE=kristobalus/nitter
echo "building image $IMAGE using buildx..."

Expand Down
2 changes: 1 addition & 1 deletion build-image.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash

# Read the version from package.json
VERSION=1.2.1
VERSION=1.3.0
IMAGE=kristobalus/nitter
echo "building image $IMAGE using buildx for multi-arch..."

Expand Down
12 changes: 3 additions & 9 deletions docker-compose.test.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
version: '3.8'
version: "3.8"

services:
nitter-redis:
image: redis:latest

nitter:
image: kristobalus/nitter:latest
image: kristobalus/nitter:latest-fly
platform: "linux/amd64"
environment:
NITTER_GUEST_ACCOUNTS_URL: "${NITTER_GUEST_ACCOUNTS_URL}"
entrypoint: |
/bin/sh -c '
cp /config/nitter.conf /src/nitter.conf
wget -O /src/guest_accounts.json $NITTER_GUEST_ACCOUNTS_URL || exit 1
./nitter
'
NITTER_GUEST_ACCOUNTS: "${NITTER_GUEST_ACCOUNTS}"
ports:
- "8080:8080"
depends_on:
Expand Down
8 changes: 5 additions & 3 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#!/bin/sh

echo "$NITTER_GUEST_ACCOUNTS_URL"
# cp /config/nitter.conf /src/nitter.conf
cp /config/nitter.conf /src/nitter.conf
cat /src/nitter.conf
wget -O /src/guest_accounts.json "$NITTER_GUEST_ACCOUNTS_URL"
echo $NITTER_GUEST_ACCOUNTS > /src/guest_accounts.json

# echo "NITTER_GUEST_ACCOUNTS_URL=$NITTER_GUEST_ACCOUNTS_URL"
# wget -O /src/guest_accounts.json "$NITTER_GUEST_ACCOUNTS_URL" || exit 1

# Redis host and port
MAX_ATTEMPTS=30 # 5 minutes with 10-second intervals
Expand Down
7 changes: 2 additions & 5 deletions fly.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
# fly.toml app configuration file generated for unmanaged-api-nitter on 2024-02-15T09:27:18+06:00
# fly.toml app configuration file generated for unmanaged-api-nitter on 2024-02-29T03:32:00+06:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#

app = 'unmanaged-api-nitter'
primary_region = 'waw'
primary_region = 'iad'

[build]
image = 'kristobalus/nitter:latest-fly'

[env]
NITTER_GUEST_ACCOUNTS_URL = 'https://guest-keeper.fly.dev/guest-accounts'

[http_service]
internal_port = 8080
force_https = true
Expand Down
5 changes: 5 additions & 0 deletions howto-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fly launch
cat .env | fly secrets import
fly scale count 1
fly deploy --ha=false
fly deploy
44 changes: 33 additions & 11 deletions src/routes/twitter_api.nim
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: AGPL-3.0-only

import json, asyncdispatch, options, uri
import json, asyncdispatch, options, uri, sugar
import times
import jester
import router_utils
Expand All @@ -10,6 +10,9 @@ import sequtils

export api

proc getQuery2*(request: Request; name: string): Query =
initQuery(params(request), name=name)

proc videoToJson*(t: Video): JsonNode =
result = newJObject()
result["durationMs"] = %t.durationMs
Expand Down Expand Up @@ -81,20 +84,20 @@ proc getUserTweetsJson*(id: string): Future[JsonNode] {.async.} =

result = response

proc searchTimeline*(query: Query; after=""): Future[string] {.async.} =
proc searchTimeline*(query: Query; after=""; count=20): Future[string] {.async.} =
let q = genQueryParam(query)
var
variables = %*{
"rawQuery": q,
"count": 20,
"count": count,
"product": "Latest",
"withDownvotePerspective": false,
"withReactionsMetadata": false,
"withReactionsPerspective": false
}
if after.len > 0:
variables["cursor"] = % after
let url = graphSearchTimeline ? {"variables": $variables, "features": gqlFeatures}
let url = graphSearchTimeline ? {"variables": $variables, "features": gqlFeatures}
result = await fetchRaw(url, Api.search)

proc getUserTweets*(id: string; after=""): Future[string] {.async.} =
Expand All @@ -105,6 +108,13 @@ proc getUserTweets*(id: string; after=""): Future[string] {.async.} =
params = {"variables": variables, "features": gqlFeatures}
result = await fetchRaw(graphUserTweets ? params, Api.userTweets)

proc getUserById*(id: string): Future[string] {.async} =
if id.len == 0 or id.any(c => not c.isDigit): return
let
variables = """{"rest_id": "$1"}""" % id
params = {"variables": variables, "features": gqlFeatures}
result = await fetchRaw(graphUserById ? params, Api.userRestId)

proc getUserReplies*(id: string; after=""): Future[string] {.async.} =
if id.len == 0: return
let
Expand Down Expand Up @@ -137,21 +147,33 @@ proc createTwitterApiRouter*(cfg: Config) =
let username = @"username"
let response = await getUserProfileJson(username)
respJson response

# get "/api/user/@id/tweets":
# let id = @"id"
# let response = await getUserTweetsJson(id)
# respJson response
get "/api/user/@id/profile":
let id = @"id"
let response = await getUserById(id)
resp Http200, { "Content-Type": "application/json" }, response

get "/api/user/@username/timeline":
let username = @"username"
let query = Query(fromUser: @[username])
let response = await searchTimeline(query)
let after = getCursor()
let count = parseInt(request.params.getOrDefault("count", "20"))
let response = await searchTimeline(query, after, count)
resp Http200, { "Content-Type": "application/json" }, response

get "/api/@name/timeline":
let names = getNames(@"name")
var query = request.getQuery2(@"name")
let after = getCursor()
let count = parseInt(request.params.getOrDefault("count", "20"))
if names.len != 1:
query.fromUser = names
let response = await searchTimeline(query, after, count)
resp Http200, { "Content-Type": "application/json" }, response

get "/api/user/@id/tweets":
let id = @"id"
let after = getCursor()
let after = getCursor()
let response = await getUserTweets(id, after)
resp Http200, { "Content-Type": "application/json" }, response

Expand Down
193 changes: 193 additions & 0 deletions twitter_login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# twitter_login.py
import requests
import base64
import json

username = "esther_mcc93968"
password = "er68Bmi9o"
TW_CONSUMER_KEY = "3nVuSoBZnx6U4vzUxf5w"
TW_CONSUMER_SECRET = "Bcs59EFbbsdF6Sl9Ng71smgStWEGwXXKSjYvPVt7qys"
TW_ANDROID_BASIC_TOKEN = "Basic {token}".format(
token=base64.b64encode(
(TW_CONSUMER_KEY + ":" + TW_CONSUMER_SECRET).encode()
).decode()
)
# print(TW_ANDROID_BASIC_TOKEN)


oauth_token = ""
oauth_token_secret = ""
authentication = None
bearer_token_req = requests.post(
"https://api.twitter.com/oauth2/token",
headers={
"Authorization": TW_ANDROID_BASIC_TOKEN,
"Content-Type": "application/x-www-form-urlencoded",
},
data="grant_type=client_credentials",
).json()
bearer_token = " ".join(str(x) for x in bearer_token_req.values())
print(bearer_token)

# The bearer token is immutable
# Bearer AAAAAAAAAAAAAAAAAAAAAFXzAwAAAAAAMHCxpeSDG1gLNLghVe8d74hl6k4%3DRUMF4xAQLsbeBhTSRrCiQpJtxoGWeyHrDb5te2jpGskWDFW82F
guest_token = requests.post(
"https://api.twitter.com/1.1/guest/activate.json",
headers={
"Authorization": bearer_token,
},
).json()["guest_token"]
print(guest_token)

twitter_header = {
"Authorization": bearer_token,
"Content-Type": "application/json",
"User-Agent": "TwitterAndroid/9.95.0-release.0 (29950000-r-0) ONEPLUS+A3010/9 (OnePlus;ONEPLUS+A3010;OnePlus;OnePlus3;0;;1;2016)",
"X-Twitter-API-Version": "5",
"X-Twitter-Client": "TwitterAndroid",
"X-Twitter-Client-Version": "9.95.0-release.0",
"OS-Version": "28",
"System-User-Agent": "Dalvik/2.1.0 (Linux; U; Android 9; ONEPLUS A3010 Build/PKQ1.181203.001)",
"X-Twitter-Active-User": "yes",
"X-Guest-Token": guest_token,
}

session = requests.Session()


task1 = session.post(
"https://api.twitter.com/1.1/onboarding/task.json",
params={
"flow_name": "login",
"api_version": "1",
"known_device_token": "",
"sim_country_code": "us",
},
json={
"flow_token": None,
"input_flow_data": {
"country_code": None,
"flow_context": {
"referrer_context": {
"referral_details": "utm_source=google-play&utm_medium=organic",
"referrer_url": "",
},
"start_location": {"location": "deeplink"},
},
"requested_variant": None,
"target_user_id": 0,
},
},
headers=twitter_header,
)

session.headers["att"] = task1.headers.get("att")
task2 = session.post(
"https://api.twitter.com/1.1/onboarding/task.json",
json={
"flow_token": task1.json().get("flow_token"),
"subtask_inputs": [
{
"enter_text": {
"suggestion_id": None,
"text": username,
"link": "next_link",
},
"subtask_id": "LoginEnterUserIdentifier",
}
],
},
headers=twitter_header,
)

task3 = session.post(
"https://api.twitter.com/1.1/onboarding/task.json",
json={
"flow_token": task2.json().get("flow_token"),
"subtask_inputs": [
{
"enter_password": {"password": password, "link": "next_link"},
"subtask_id": "LoginEnterPassword",
}
],
},
headers=twitter_header,
)

task4 = session.post(
"https://api.twitter.com/1.1/onboarding/task.json",
json={
"flow_token": task3.json().get("flow_token"),
"subtask_inputs": [
{
"check_logged_in_account": {"link": "AccountDuplicationCheck_false"},
"subtask_id": "AccountDuplicationCheck",
}
],
},
headers=twitter_header,
).json()

for t4_subtask in task4.get("subtasks", []):
if "open_account" in t4_subtask:
authentication = t4_subtask["open_account"]
break
elif "enter_text" in t4_subtask:
response_text = t4_subtask["enter_text"]["hint_text"]
code = input(f"Requesting {response_text}: ")
task5 = session.post(
"https://api.twitter.com/1.1/onboarding/task.json",
json={
"flow_token": task4.get("flow_token"),
"subtask_inputs": [
{
"enter_text": {
"suggestion_id": None,
"text": code,
"link": "next_link",
},
"subtask_id": "LoginTwoFactorAuthChallenge",
}
],
},
headers=twitter_header,
).json()
print(task5)
for t5_subtask in task5.get("subtasks", []):
print(t5_subtask)
if "open_account" in t5_subtask:
authentication = t5_subtask["open_account"]
oauth_token = t5_subtask["open_account"]["oauth_token"]
oauth_token_secret = t5_subtask["open_account"]["oauth_token_secret"]

print(authentication)

# Constructing the new list of dictionaries with the extracted values
output_list = [{
"oauth_token": oauth_token,
"oauth_token_secret": oauth_token_secret
}]

# Converting the list back to a JSON string
output_json = json.dumps(output_list)

# Printing the result
print(output_json)

# {
# 'attribution_event': 'login',
# 'known_device_token': 'XXXXXXXXXXXXXXXXXXXXXX',
# 'next_link': {
# 'link_id': 'next_link',
# 'link_type': 'subtask',
# 'subtask_id': 'SuccessExit'
# },
# 'oauth_token': 'XXXXXXXXXXXXXXXXXXXXXX',
# 'oauth_token_secret': 'XXXXXXXXXXXXXXXXXXXXXX',
# 'user': {
# 'id': 'XXXXXXXXXXXXXXXXXXXXXX',
# 'id_str': 'XXXXXXXXXXXXXXXXXXXXXX',
# 'name': 'XXXXXXXXXXXXXXXXXXXXXX',
# 'screen_name': 'XXXXXXXXXXXXXXXXXXXXXX'
# }
# }
Loading

0 comments on commit b9d38a3

Please sign in to comment.