Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed some login issues and others #646

Open
wants to merge 7 commits into
base: v1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions fbchat/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def __init__(
self,
email,
password,
user_agent=None,
user_agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.61 Safari/537.36",
max_tries=5,
session_cookies=None,
logging_level=logging.INFO,
Expand Down Expand Up @@ -312,7 +312,9 @@ def fetchThreads(self, thread_location, before=None, after=None, limit=None):
before=last_thread_timestamp, thread_location=thread_location
)

if len(candidates) > 1:
if last_thread_timestamp == None and len(candidates) >= 1:
threads += candidates
elif len(candidates) > 1:
threads += candidates[1:]
else: # End of threads
break
Expand Down Expand Up @@ -1714,7 +1716,7 @@ def reactToMessage(self, message_id, reaction):
"client_mutation_id": "1",
"actor_id": self._uid,
"message_id": str(message_id),
"reaction": reaction.value if reaction else None,
"reaction": reaction if reaction else None
}
data = {"doc_id": 1491398900900362, "variables": json.dumps({"data": data})}
j = self._payload_post("/webgraphql/mutation", data)
Expand Down Expand Up @@ -2614,14 +2616,18 @@ def getThreadIdAndThreadType(msg_metadata):
thread_id, thread_type = getThreadIdAndThreadType(i)
mid = i["messageId"]
author_id = str(i["userId"])
reaction = (
MessageReaction(i["reaction"]) if i.get("reaction") else None
)

# Commented as the statement below invalidates other reaction emoji

# reaction = (reaction = (
# MessageReaction(i["reaction"]) if i.get("reaction") else None
# )

add_reaction = not bool(i["action"])
if add_reaction:
self.onReactionAdded(
mid=mid,
reaction=reaction,
reaction=i.get("reaction"),
author_id=author_id,
thread_id=thread_id,
thread_type=thread_type,
Expand Down
81 changes: 66 additions & 15 deletions fbchat/_state.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: UTF-8 -*-
from __future__ import unicode_literals
from pprint import pprint

import attr
import bs4
Expand All @@ -15,22 +16,26 @@
def get_user_id(session):
# TODO: Optimize this `.get_dict()` call!
rtn = session.cookies.get_dict().get("c_user")
# Sometimes c_user is missing if logged in from another location
if rtn is None:
rtn = str(input("c_user is blank, enter manually from browser cookies: "))
if rtn is None:
raise _exception.FBchatException("Could not find user id")
return str(rtn)


def find_input_fields(html):
return bs4.BeautifulSoup(html, "html.parser", parse_only=bs4.SoupStrainer("input"))
# return bs4.BeautifulSoup(html, "html.parser", parse_only=bs4.SoupStrainer("input"))
return bs4.BeautifulSoup(html, "html.parser")


def session_factory(user_agent=None):
session = requests.session()
session.headers["Referer"] = "https://www.facebook.com"
session.headers["Accept"] = "text/html"
session = requests.Session()
# session.headers["Referer"] = "https://www.facebook.com"
# session.headers["Accept"] = "text/html"

# TODO: Deprecate setting the user agent manually
session.headers["User-Agent"] = user_agent or random.choice(_util.USER_AGENTS)
# session.headers["User-Agent"] = user_agent or random.choice(_util.USER_AGENTS)
return session


Expand Down Expand Up @@ -123,20 +128,29 @@ def get_params(self):
}

@classmethod
def login(cls, email, password, on_2fa_callback, user_agent=None):
def login(cls, email, password, on_2fa_callback, user_agent=_util.USER_AGENTS):
session = session_factory(user_agent=user_agent)

soup = find_input_fields(session.get("https://m.facebook.com/").text)

soup = soup.find_all("input")

data = dict(
(elem["name"], elem["value"])
for elem in soup
if elem.has_attr("value") and elem.has_attr("name")
)

data["email"] = email
data["pass"] = password
data["login"] = "Log In"

r = session.post("https://m.facebook.com/login.php?login_attempt=1", data=data)
# pprint(data)
r = session.post("https://m.facebook.com/login/device-based/regular/login/?refsrc=deprecated&lwv=100&refid=8", data=data)

# print(r.status_code)
# print(r.url)
# print(r.content)

# Usually, 'Checkpoint' will refer to 2FA
if "checkpoint" in r.url and ('id="approvals_code"' in r.text.lower()):
Expand All @@ -147,6 +161,10 @@ def login(cls, email, password, on_2fa_callback, user_agent=None):
if "save-device" in r.url:
r = session.get("https://m.facebook.com/login/save-device/cancel/")

# Sometimes facebook redirects to facebook.com/cookie/consent-page/*[...more directories]. So, go to homepage
if "cookie" in r.url:
r = session.get("https://m.facebook.com/", allow_redirects=False)

if is_home(r.url):
return cls.from_session(session=session)
else:
Expand Down Expand Up @@ -177,20 +195,53 @@ def from_session(cls, session):
user_id = get_user_id(session)

r = session.get(_util.prefix_url("/"))

soup = find_input_fields(r.text)

fb_dtsg_element = soup.find("input", {"name": "fb_dtsg"})
if fb_dtsg_element:
fb_dtsg = fb_dtsg_element["value"]
else:
fb_dtsg = ''

if fb_dtsg == None or fb_dtsg == "":
FB_DTSG_REGEX = re.compile(r'"[a-zA-Z0-9-_:]+:+[a-zA-Z0-9-_:]*"')
fb_dtsg = FB_DTSG_REGEX.search(r.text).group(0)
fb_dtsg = fb_dtsg.replace('"', "")
fb_dtsg = fb_dtsg.replace("'", '')
# print("\n\n[latest] fb_dtsg:\n")
# print(fb_dtsg)


if fb_dtsg == None or fb_dtsg == "":
fb_dtsg_element = soup.find("input", {"name": "fb_dtsg"})
print("\n\n[1]fb_dtsg_element:\n")
print(fb_dtsg_element)
if fb_dtsg_element:
fb_dtsg = fb_dtsg_element["value"]
# print("\n\n[1.1]fb_dtsg_element:\n")
# print(fb_dtsg)

if fb_dtsg == None or fb_dtsg == "":
# Fall back to searching with a regex
fb_dtsg = FB_DTSG_REGEX.search(r.text).group(1)

revision = int(r.text.split('"client_revision":', 1)[1].split(",", 1)[0])
fb_dtsg = ''
try:
fb_dtsg = FB_DTSG_REGEX.search(r.text).group(1)
# print("\n\n[2]fb_dtsg_element:\n")
# print(fb_dtsg)
except:
pass


if fb_dtsg == None or fb_dtsg == "":
FB_DTSG_REGEX = re.compile(r'"[a-zA-Z0-9_.-]*:[a-zA-Z0-9_.-]*"\)')
fb_dtsg = FB_DTSG_REGEX.search(r.text).group(0)
fb_dtsg = fb_dtsg.replace(")", "")
fb_dtsg = fb_dtsg.replace('"', '')
# print("\n\n[3]fb_dtsg_element:\n")
# print(fb_dtsg)

revision = int(r.text.split('"client_revision":')[1].split(",", 1)[0])

logout_h_element = soup.find("input", {"name": "h"})
logout_h = logout_h_element["value"] if logout_h_element else None

print(user_id, fb_dtsg, revision, logout_h)

return cls(
user_id=user_id,
Expand Down
10 changes: 2 additions & 8 deletions fbchat/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,8 @@
log.addHandler(handler)

#: Default list of user agents
USER_AGENTS = [
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/601.1.10 (KHTML, like Gecko) Version/8.0.5 Safari/601.1.10",
"Mozilla/5.0 (Windows NT 6.3; WOW64; ; NCT50_AAP285C84A1328) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
]
# Changing to this user-agent solved login issue for some people.
USER_AGENTS = ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36"]


def now():
Expand Down