Make Session public

This commit is contained in:
Mads Marquart
2020-01-08 10:33:25 +01:00
parent 092573fcbb
commit 41f1007936
3 changed files with 55 additions and 12 deletions

View File

@@ -13,6 +13,7 @@ _logging.getLogger(__name__).addHandler(_logging.NullHandler())
from . import _core, _util
from ._core import Image
from ._exception import FBchatException, FBchatFacebookError
from ._session import Session
from ._thread import ThreadType, ThreadLocation, ThreadColor, Thread
from ._user import TypingStatus, User, ActiveStatus
from ._group import Group
@@ -44,4 +45,4 @@ __license__ = "BSD 3-Clause"
__author__ = "Taehoon Kim; Moreels Pieter-Jan; Mads Marquart"
__email__ = "carpedm20@gmail.com"
__all__ = ("Client",)
__all__ = ("Session", "Client")

View File

@@ -38,9 +38,9 @@ ACONTEXT = {
class Client:
"""A client for the Facebook Chat (Messenger).
This is the main class, which contains all the methods you use to interact with
Facebook. You can extend this class, and overwrite the ``on`` methods, to provide
custom event handling (mainly useful while listening).
This contains all the methods you use to interact with Facebook. You can extend this
class, and overwrite the ``on`` methods, to provide custom event handling (mainly
useful while listening).
"""
@property

View File

@@ -100,7 +100,10 @@ def _2fa_helper(session, code, r):
@attrs_default
class Session:
"""Stores and manages state required for most Facebook requests."""
"""Stores and manages state required for most Facebook requests.
This is the main class, which is used to login to Facebook.
"""
user_id = attr.ib()
_fb_dtsg = attr.ib()
@@ -110,7 +113,7 @@ class Session:
_client_id = attr.ib(factory=client_id_factory)
_logout_h = attr.ib(None)
def get_params(self):
def _get_params(self):
self._counter += 1 # TODO: Make this operation atomic / thread-safe
return {
"__a": 1,
@@ -121,6 +124,16 @@ class Session:
@classmethod
def login(cls, email, password, on_2fa_callback):
"""Login the user, using ``email`` and ``password``.
Args:
email: Facebook ``email`` or ``id`` or ``phone number``
password: Facebook account password
on_2fa_callback: Function that will be called, in case a 2FA code is needed
Raises:
FBchatException: On failed login
"""
session = session_factory()
soup = find_input_fields(session.get("https://m.facebook.com/").text)
@@ -145,7 +158,7 @@ class Session:
r = session.get("https://m.facebook.com/login/save-device/cancel/")
if is_home(r.url):
return cls.from_session(session=session)
return cls._from_session(session=session)
else:
raise _exception.FBchatException(
"Login failed. Check email/password. "
@@ -153,12 +166,24 @@ class Session:
)
def is_logged_in(self):
"""Send a request to Facebook to check the login status.
Returns:
bool: Whether the user is still logged in
"""
# Send a request to the login url, to see if we're directed to the home page
url = "https://m.facebook.com/login.php?login_attempt=1"
r = self._session.get(url, allow_redirects=False)
return "Location" in r.headers and is_home(r.headers["Location"])
def logout(self):
"""Safely log out the user.
The session object must not be used after this action has been performed!
Raises:
FBchatException: On failed logout
"""
logout_h = self._logout_h
if not logout_h:
url = _util.prefix_url("/bluebar/modern_settings_menu/")
@@ -166,10 +191,14 @@ class Session:
logout_h = re.search(r'name=\\"h\\" value=\\"(.*?)\\"', h_r.text).group(1)
url = _util.prefix_url("/logout.php")
return self._session.get(url, params={"ref": "mb", "h": logout_h}).ok
r = self._session.get(url, params={"ref": "mb", "h": logout_h})
if not r.ok:
raise exception.FBchatException(
"Failed logging out: {}".format(r.status_code)
)
@classmethod
def from_session(cls, session):
def _from_session(cls, session):
# TODO: Automatically set user_id when the cookie changes in the session
user_id = get_user_id(session)
@@ -198,22 +227,35 @@ class Session:
)
def get_cookies(self):
"""Retrieve session cookies, that can later be used in `from_cookies`.
Returns:
dict: A dictionary containing session cookies
"""
return self._session.cookies.get_dict()
@classmethod
def from_cookies(cls, cookies):
"""Load a session from session cookies.
Args:
cookies (dict): A dictionary containing session cookies
Raises:
FBchatException: If given invalid cookies
"""
session = session_factory()
session.cookies = requests.cookies.merge_cookies(session.cookies, cookies)
return cls.from_session(session=session)
return cls._from_session(session=session)
def _get(self, url, params, error_retries=3):
params.update(self.get_params())
params.update(self._get_params())
r = self._session.get(_util.prefix_url(url), params=params)
content = _util.check_request(r)
return _util.to_json(content)
def _post(self, url, data, files=None, as_graphql=False):
data.update(self.get_params())
data.update(self._get_params())
r = self._session.post(_util.prefix_url(url), data=data, files=files)
content = _util.check_request(r)
if as_graphql: