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

View File

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

View File

@@ -100,7 +100,10 @@ def _2fa_helper(session, code, r):
@attrs_default @attrs_default
class Session: 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() user_id = attr.ib()
_fb_dtsg = attr.ib() _fb_dtsg = attr.ib()
@@ -110,7 +113,7 @@ class Session:
_client_id = attr.ib(factory=client_id_factory) _client_id = attr.ib(factory=client_id_factory)
_logout_h = attr.ib(None) _logout_h = attr.ib(None)
def get_params(self): def _get_params(self):
self._counter += 1 # TODO: Make this operation atomic / thread-safe self._counter += 1 # TODO: Make this operation atomic / thread-safe
return { return {
"__a": 1, "__a": 1,
@@ -121,6 +124,16 @@ class Session:
@classmethod @classmethod
def login(cls, email, password, on_2fa_callback): 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() session = session_factory()
soup = find_input_fields(session.get("https://m.facebook.com/").text) 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/") r = session.get("https://m.facebook.com/login/save-device/cancel/")
if is_home(r.url): if is_home(r.url):
return cls.from_session(session=session) return cls._from_session(session=session)
else: else:
raise _exception.FBchatException( raise _exception.FBchatException(
"Login failed. Check email/password. " "Login failed. Check email/password. "
@@ -153,12 +166,24 @@ class Session:
) )
def is_logged_in(self): 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 # 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" url = "https://m.facebook.com/login.php?login_attempt=1"
r = self._session.get(url, allow_redirects=False) r = self._session.get(url, allow_redirects=False)
return "Location" in r.headers and is_home(r.headers["Location"]) return "Location" in r.headers and is_home(r.headers["Location"])
def logout(self): 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 logout_h = self._logout_h
if not logout_h: if not logout_h:
url = _util.prefix_url("/bluebar/modern_settings_menu/") 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) logout_h = re.search(r'name=\\"h\\" value=\\"(.*?)\\"', h_r.text).group(1)
url = _util.prefix_url("/logout.php") 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 @classmethod
def from_session(cls, session): def _from_session(cls, session):
# TODO: Automatically set user_id when the cookie changes in the session # TODO: Automatically set user_id when the cookie changes in the session
user_id = get_user_id(session) user_id = get_user_id(session)
@@ -198,22 +227,35 @@ class Session:
) )
def get_cookies(self): 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() return self._session.cookies.get_dict()
@classmethod @classmethod
def from_cookies(cls, cookies): 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 = session_factory()
session.cookies = requests.cookies.merge_cookies(session.cookies, cookies) 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): 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) r = self._session.get(_util.prefix_url(url), params=params)
content = _util.check_request(r) content = _util.check_request(r)
return _util.to_json(content) return _util.to_json(content)
def _post(self, url, data, files=None, as_graphql=False): 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) r = self._session.post(_util.prefix_url(url), data=data, files=files)
content = _util.check_request(r) content = _util.check_request(r)
if as_graphql: if as_graphql: