Merge pull request #466 from carpedm20/various-removals

Various removals
This commit is contained in:
Mads Marquart
2019-09-08 18:51:20 +02:00
committed by GitHub
11 changed files with 57 additions and 348 deletions

View File

@@ -21,8 +21,8 @@ Traceback (most recent call last):
File "[site-packages]/fbchat/client.py", line 78, in __init__
self.login(email, password, max_tries)
File "[site-packages]/fbchat/client.py", line 407, in login
raise FBchatUserError('Login failed. Check email/password. (Failed on URL: {})'.format(login_url))
fbchat.models.FBchatUserError: Login failed. Check email/password. (Failed on URL: https://m.facebook.com/login.php?login_attempt=1)
raise FBchatException('Login failed. Check email/password. (Failed on URL: {})'.format(login_url))
fbchat.FBchatException: Login failed. Check email/password. (Failed on URL: https://m.facebook.com/login.php?login_attempt=1)
```
## Environment information

View File

@@ -38,7 +38,6 @@ Exceptions
.. autoexception:: FBchatException()
.. autoexception:: FBchatFacebookError()
.. autoexception:: FBchatUserError()
Attachments
-----------

View File

@@ -11,10 +11,10 @@ _logging.getLogger(__name__).addHandler(_logging.NullHandler())
# The order of these is somewhat significant, e.g. User has to be imported after Thread!
from . import _core, _util
from ._exception import FBchatException, FBchatFacebookError, FBchatUserError
from ._exception import FBchatException, FBchatFacebookError
from ._thread import ThreadType, ThreadLocation, ThreadColor, Thread
from ._user import TypingStatus, User, ActiveStatus
from ._group import Group, Room
from ._group import Group
from ._page import Page
from ._message import EmojiSize, MessageReaction, Mention, Message
from ._attachment import Attachment, UnsentMessage, ShareAttachment

View File

@@ -7,7 +7,7 @@ from collections import OrderedDict
from ._core import log
from . import _util, _graphql, _state
from ._exception import FBchatException, FBchatFacebookError, FBchatUserError
from ._exception import FBchatException, FBchatFacebookError
from ._thread import ThreadType, ThreadLocation, ThreadColor
from ._user import TypingStatus, User, ActiveStatus
from ._group import Group
@@ -43,25 +43,6 @@ class Client:
to provide custom event handling (mainly useful while listening).
"""
listening = False
"""Whether the client is listening.
Used when creating an external event loop to determine when to stop listening.
"""
@property
def ssl_verify(self):
"""Verify SSL certificate.
Set to False to allow debugging with a proxy.
"""
# TODO: Deprecate this
return self._state._session.verify
@ssl_verify.setter
def ssl_verify(self, value):
self._state._session.verify = value
@property
def uid(self):
"""The ID of the client.
@@ -70,16 +51,12 @@ class Client:
"""
return self._uid
def __init__(
self, email, password, user_agent=None, max_tries=5, session_cookies=None
):
def __init__(self, email, password, session_cookies=None):
"""Initialize and log in the client.
Args:
email: Facebook ``email``, ``id`` or ``phone number``
password: Facebook account password
user_agent: Custom user agent to use when sending requests. If `None`, user agent will be chosen from a premade list
max_tries (int): Maximum number of times to try logging in
session_cookies (dict): Cookies from a previous session (Will default to login if these are invalid)
Raises:
@@ -87,8 +64,6 @@ class Client:
"""
self._sticky, self._pool = (None, None)
self._seq = "0"
self._default_thread_id = None
self._default_thread_type = None
self._pull_channel = 0
self._markAlive = True
self._buddylist = dict()
@@ -96,10 +71,10 @@ class Client:
# If session cookies aren't set, not properly loaded or gives us an invalid session, then do the login
if (
not session_cookies
or not self.setSession(session_cookies, user_agent=user_agent)
or not self.setSession(session_cookies)
or not self.isLoggedIn()
):
self.login(email, password, max_tries, user_agent=user_agent)
self.login(email, password)
"""
INTERNAL REQUEST METHODS
@@ -160,7 +135,7 @@ class Client:
"""
return self._state.get_cookies()
def setSession(self, session_cookies, user_agent=None):
def setSession(self, session_cookies):
"""Load session cookies.
Args:
@@ -171,16 +146,14 @@ class Client:
"""
try:
# Load cookies into current session
self._state = _state.State.from_cookies(
session_cookies, user_agent=user_agent
)
self._state = _state.State.from_cookies(session_cookies)
self._uid = self._state.user_id
except Exception as e:
log.exception("Failed loading session")
return False
return True
def login(self, email, password, max_tries=5, user_agent=None):
def login(self, email, password):
"""Login the user, using ``email`` and ``password``.
If the user is already logged in, this will do a re-login.
@@ -188,36 +161,20 @@ class Client:
Args:
email: Facebook ``email`` or ``id`` or ``phone number``
password: Facebook account password
max_tries (int): Maximum number of times to try logging in
Raises:
FBchatException: On failed login
"""
self.onLoggingIn(email=email)
if max_tries < 1:
raise FBchatUserError("Cannot login: max_tries should be at least one")
if not (email and password):
raise FBchatUserError("Email and password not set")
raise ValueError("Email and password not set")
for i in range(1, max_tries + 1):
try:
self._state = _state.State.login(
email,
password,
on_2fa_callback=self.on2FACode,
user_agent=user_agent,
email, password, on_2fa_callback=self.on2FACode
)
self._uid = self._state.user_id
except Exception:
if i >= max_tries:
raise
log.exception("Attempt #{} failed, retrying".format(i))
time.sleep(1)
else:
self.onLoggedIn(email=email)
break
def logout(self):
"""Safely log out the client.
@@ -235,45 +192,6 @@ class Client:
END LOGIN METHODS
"""
"""
DEFAULT THREAD METHODS
"""
def _getThread(self, given_thread_id=None, given_thread_type=None):
"""Check if thread ID is given and if default is set, and return correct values.
Returns:
tuple: Thread ID and thread type
Raises:
ValueError: If thread ID is not given and there is no default
"""
if given_thread_id is None:
if self._default_thread_id is not None:
return self._default_thread_id, self._default_thread_type
else:
raise ValueError("Thread ID is not set")
else:
return given_thread_id, given_thread_type
def setDefaultThread(self, thread_id, thread_type):
"""Set default thread to send messages to.
Args:
thread_id: User/Group ID to default to. See :ref:`intro_threads`
thread_type (ThreadType): See :ref:`intro_threads`
"""
self._default_thread_id = thread_id
self._default_thread_type = thread_type
def resetDefaultThread(self):
"""Reset default thread."""
self.setDefaultThread(None, None)
"""
END DEFAULT THREAD METHODS
"""
"""
FETCH METHODS
"""
@@ -494,8 +412,6 @@ class Client:
Raises:
FBchatException: If request failed
"""
thread_id, thread_type = self._getThread(thread_id, None)
data = {
"query": query,
"snippetOffset": offset,
@@ -624,7 +540,7 @@ class Client:
if thread.type == ThreadType.USER:
users[id_] = thread
else:
raise FBchatUserError("Thread {} was not a user".format(thread))
raise ValueError("Thread {} was not a user".format(thread))
return users
@@ -649,7 +565,7 @@ class Client:
if thread.type == ThreadType.PAGE:
pages[id_] = thread
else:
raise FBchatUserError("Thread {} was not a page".format(thread))
raise ValueError("Thread {} was not a page".format(thread))
return pages
@@ -671,7 +587,7 @@ class Client:
if thread.type == ThreadType.GROUP:
groups[id_] = thread
else:
raise FBchatUserError("Thread {} was not a group".format(thread))
raise ValueError("Thread {} was not a group".format(thread))
return groups
@@ -756,8 +672,6 @@ class Client:
Raises:
FBchatException: If request failed
"""
thread_id, thread_type = self._getThread(thread_id, None)
params = {
"id": thread_id,
"message_limit": limit,
@@ -789,12 +703,11 @@ class Client:
return messages
def fetchThreadList(
self, offset=None, limit=20, thread_location=ThreadLocation.INBOX, before=None
self, limit=20, thread_location=ThreadLocation.INBOX, before=None
):
"""Fetch the client's thread list.
Args:
offset: Deprecated. Do not use!
limit (int): Max. number of threads to retrieve. Capped at 20
thread_location (ThreadLocation): INBOX, PENDING, ARCHIVED or OTHER
before (datetime.datetime): The point from which to retrieve threads
@@ -805,20 +718,13 @@ class Client:
Raises:
FBchatException: If request failed
"""
if offset is not None:
log.warning(
"Using `offset` in `fetchThreadList` is no longer supported, "
"since Facebook migrated to the use of GraphQL in this request. "
"Use `before` instead."
)
if limit > 20 or limit < 1:
raise FBchatUserError("`limit` should be between 1 and 20")
raise ValueError("`limit` should be between 1 and 20")
if thread_location in ThreadLocation:
loc_str = thread_location.value
else:
raise FBchatUserError('"thread_location" must be a value of ThreadLocation')
raise TypeError('"thread_location" must be a value of ThreadLocation')
params = {
"limit": limit,
@@ -891,6 +797,7 @@ class Client:
image_id = str(image_id)
data = {"photo_id": str(image_id)}
j = self._post("/mercury/attachments/photo/", data)
_util.handle_payload_error(j)
url = _util.get_jsmods_require(j, 3)
if url is None:
@@ -910,7 +817,6 @@ class Client:
Raises:
FBchatException: If request failed
"""
thread_id, thread_type = self._getThread(thread_id, None)
message_info = self._forcedFetch(thread_id, mid).get("message")
return Message._from_graphql(message_info)
@@ -995,7 +901,6 @@ class Client:
Returns:
typing.Iterable: :class:`ImageAttachment` or :class:`VideoAttachment`
"""
thread_id, thread_type = self._getThread(thread_id, None)
data = {"id": thread_id, "first": 48}
thread_id = str(thread_id)
j, = self.graphql_requests(_graphql.from_query_id("515216185516880", data))
@@ -1057,32 +962,11 @@ class Client:
Raises:
FBchatException: If request failed
"""
thread_id, thread_type = self._getThread(thread_id, thread_type)
thread = thread_type._to_class()(thread_id)
data = thread._to_send_data()
data.update(message._to_send_data())
return self._doSendRequest(data)
def sendMessage(self, message, thread_id=None, thread_type=ThreadType.USER):
"""Deprecated. Use :func:`fbchat.Client.send` instead."""
return self.send(
Message(text=message), thread_id=thread_id, thread_type=thread_type
)
def sendEmoji(
self,
emoji=None,
size=EmojiSize.SMALL,
thread_id=None,
thread_type=ThreadType.USER,
):
"""Deprecated. Use :func:`fbchat.Client.send` instead."""
return self.send(
Message(text=emoji, emoji_size=size),
thread_id=thread_id,
thread_type=thread_type,
)
def wave(self, wave_first=True, thread_id=None, thread_type=None):
"""Wave hello to a thread.
@@ -1097,7 +981,6 @@ class Client:
Raises:
FBchatException: If request failed
"""
thread_id, thread_type = self._getThread(thread_id, thread_type)
thread = thread_type._to_class()(thread_id)
data = thread._to_send_data()
data["action_type"] = "ma-type:user-generated-message"
@@ -1131,7 +1014,7 @@ class Client:
)
elif isinstance(quick_reply, QuickReplyLocation):
if not isinstance(payload, LocationAttachment):
raise ValueError(
raise TypeError(
"Payload must be an instance of `fbchat.LocationAttachment`"
)
return self.sendLocation(
@@ -1162,7 +1045,6 @@ class Client:
def _sendLocation(
self, location, current=True, message=None, thread_id=None, thread_type=None
):
thread_id, thread_type = self._getThread(thread_id, thread_type)
thread = thread_type._to_class()(thread_id)
data = thread._to_send_data()
if message is not None:
@@ -1231,7 +1113,6 @@ class Client:
`files` should be a list of tuples, with a file's ID and mimetype.
"""
thread_id, thread_type = self._getThread(thread_id, thread_type)
thread = thread_type._to_class()(thread_id)
data = thread._to_send_data()
data.update(self._oldMessage(message)._to_send_data())
@@ -1337,48 +1218,6 @@ class Client:
files=files, message=message, thread_id=thread_id, thread_type=thread_type
)
def sendImage(
self,
image_id,
message=None,
thread_id=None,
thread_type=ThreadType.USER,
is_gif=False,
):
"""Deprecated."""
if is_gif:
mimetype = "image/gif"
else:
mimetype = "image/png"
return self._sendFiles(
files=[(image_id, mimetype)],
message=message,
thread_id=thread_id,
thread_type=thread_type,
)
def sendRemoteImage(
self, image_url, message=None, thread_id=None, thread_type=ThreadType.USER
):
"""Deprecated. Use :func:`fbchat.Client.sendRemoteFiles` instead."""
return self.sendRemoteFiles(
file_urls=[image_url],
message=message,
thread_id=thread_id,
thread_type=thread_type,
)
def sendLocalImage(
self, image_path, message=None, thread_id=None, thread_type=ThreadType.USER
):
"""Deprecated. Use :func:`fbchat.Client.sendLocalFiles` instead."""
return self.sendLocalFiles(
file_paths=[image_path],
message=message,
thread_id=thread_id,
thread_type=thread_type,
)
def forwardAttachment(self, attachment_id, thread_id=None):
"""Forward an attachment.
@@ -1389,7 +1228,6 @@ class Client:
Raises:
FBchatException: If request failed
"""
thread_id, thread_type = self._getThread(thread_id, None)
data = {
"attachment_id": attachment_id,
"recipient_map[{}]".format(_util.generateOfflineThreadingID()): thread_id,
@@ -1417,7 +1255,7 @@ class Client:
data = self._oldMessage(message)._to_send_data()
if len(user_ids) < 2:
raise FBchatUserError("Error when creating group: Not enough participants")
raise ValueError("Error when creating group: Not enough participants")
for i, user_id in enumerate(user_ids + [self._uid]):
data["specific_to_list[{}]".format(i)] = "fbid:{}".format(user_id)
@@ -1439,7 +1277,6 @@ class Client:
Raises:
FBchatException: If request failed
"""
thread_id, thread_type = self._getThread(thread_id, None)
data = Group(thread_id)._to_send_data()
data["action_type"] = "ma-type:log-message"
@@ -1449,7 +1286,7 @@ class Client:
for i, user_id in enumerate(user_ids):
if user_id == self._uid:
raise FBchatUserError(
raise ValueError(
"Error when adding users: Cannot add self to group thread"
)
else:
@@ -1469,14 +1306,10 @@ class Client:
Raises:
FBchatException: If request failed
"""
thread_id, thread_type = self._getThread(thread_id, None)
data = {"uid": user_id, "tid": thread_id}
j = self._payload_post("/chat/remove_participants/", data)
def _adminStatus(self, admin_ids, admin, thread_id=None):
thread_id, thread_type = self._getThread(thread_id, None)
data = {"add": admin, "thread_fbid": thread_id}
admin_ids = _util.require_list(admin_ids)
@@ -1520,14 +1353,10 @@ class Client:
Raises:
FBchatException: If request failed
"""
thread_id, thread_type = self._getThread(thread_id, None)
data = {"set_mode": int(require_admin_approval), "thread_fbid": thread_id}
j = self._payload_post("/messaging/set_approval_mode/?dpr=1", data)
def _usersApproval(self, user_ids, approve, thread_id=None):
thread_id, thread_type = self._getThread(thread_id, None)
user_ids = _util.require_list(user_ids)
data = {
@@ -1576,8 +1405,6 @@ class Client:
Raises:
FBchatException: If request failed
"""
thread_id, thread_type = self._getThread(thread_id, None)
data = {"thread_image_id": image_id, "thread_id": thread_id}
j = self._payload_post("/messaging/set_thread_image/?dpr=1", data)
@@ -1625,8 +1452,6 @@ class Client:
Raises:
FBchatException: If request failed
"""
thread_id, thread_type = self._getThread(thread_id, thread_type)
if thread_type == ThreadType.USER:
# The thread is a user, so we change the user's nickname
return self.changeNickname(
@@ -1650,8 +1475,6 @@ class Client:
Raises:
FBchatException: If request failed
"""
thread_id, thread_type = self._getThread(thread_id, thread_type)
data = {
"nickname": nickname,
"participant_id": user_id,
@@ -1671,8 +1494,6 @@ class Client:
Raises:
FBchatException: If request failed
"""
thread_id, thread_type = self._getThread(thread_id, None)
data = {
"color_choice": color.value if color != ThreadColor.MESSENGER_BLUE else "",
"thread_or_other_fbid": thread_id,
@@ -1695,8 +1516,6 @@ class Client:
Raises:
FBchatException: If request failed
"""
thread_id, thread_type = self._getThread(thread_id, None)
data = {"emoji_choice": emoji, "thread_or_other_fbid": thread_id}
j = self._payload_post(
"/messaging/save_thread_emoji/?source=thread_settings&dpr=1", data
@@ -1733,8 +1552,6 @@ class Client:
Raises:
FBchatException: If request failed
"""
thread_id, thread_type = self._getThread(thread_id, None)
data = {
"event_type": "EVENT",
"event_time": _util.datetime_to_seconds(plan.time),
@@ -1801,11 +1618,6 @@ class Client:
}
j = self._payload_post("/ajax/eventreminder/rsvp", data)
def eventReminder(self, thread_id, time, title, location="", location_id=""):
"""Deprecated. Use :func:`fbchat.Client.createPlan` instead."""
plan = Plan(time=time, title=title, location=location, location_id=location_id)
self.createPlan(plan=plan, thread_id=thread_id)
def createPoll(self, poll, thread_id=None):
"""Create poll in a group thread.
@@ -1816,8 +1628,6 @@ class Client:
Raises:
FBchatException: If request failed
"""
thread_id, thread_type = self._getThread(thread_id, None)
# We're using ordered dictionaries, because the Facebook endpoint that parses
# the POST parameters is badly implemented, and deals with ordering the options
# wrongly. If you can find a way to fix this for the endpoint, or if you find
@@ -1874,8 +1684,6 @@ class Client:
Raises:
FBchatException: If request failed
"""
thread_id, thread_type = self._getThread(thread_id, thread_type)
data = {
"typ": status.value,
"thread": thread_id,
@@ -2087,7 +1895,6 @@ class Client:
Raises:
FBchatException: If request failed
"""
thread_id, thread_type = self._getThread(thread_id, None)
j = self._payload_post("/ajax/mercury/mark_spam.php?dpr=1", {"id": thread_id})
return True
@@ -2117,7 +1924,6 @@ class Client:
mute_time (datetime.timedelta): Time to mute, use ``None`` to mute forever
thread_id: User/Group ID to mute. See :ref:`intro_threads`
"""
thread_id, thread_type = self._getThread(thread_id, None)
if mute_time is None:
mute_settings = -1
else:
@@ -2140,7 +1946,6 @@ class Client:
mute: Boolean. True to mute, False to unmute
thread_id: User/Group ID to mute. See :ref:`intro_threads`
"""
thread_id, thread_type = self._getThread(thread_id, None)
data = {"reactions_mute_mode": int(mute), "thread_fbid": thread_id}
j = self._payload_post(
"/ajax/mercury/change_reactions_mute_thread/?dpr=1", data
@@ -2161,7 +1966,6 @@ class Client:
mute: Boolean. True to mute, False to unmute
thread_id: User/Group ID to mute. See :ref:`intro_threads`
"""
thread_id, thread_type = self._getThread(thread_id, None)
data = {"mentions_mute_mode": int(mute), "thread_fbid": thread_id}
j = self._payload_post("/ajax/mercury/change_mentions_mute_thread/?dpr=1", data)
@@ -2194,6 +1998,7 @@ class Client:
"https://{}-edge-chat.facebook.com/active_ping".format(self._pull_channel),
data,
)
_util.handle_payload_error(j)
def _pullMessage(self):
"""Call pull api to fetch message data."""
@@ -2205,9 +2010,11 @@ class Client:
"clientid": self._state._client_id,
"state": "active" if self._markAlive else "offline",
}
return self._get(
j = self._get(
"https://{}-edge-chat.facebook.com/pull".format(self._pull_channel), data
)
_util.handle_payload_error(j)
return j
def _parseDelta(self, m):
def getThreadIdAndThreadType(msg_metadata):
@@ -2728,7 +2535,6 @@ class Client:
self.onMessage(
mid=message.uid,
author_id=message.author,
message=message.text,
message_object=message,
thread_id=thread_id,
thread_type=thread_type,
@@ -2743,7 +2549,6 @@ class Client:
self.onMessage(
mid=mid,
author_id=author_id,
message=delta.get("body", ""),
message_object=Message._from_pull(
delta,
mid=mid,
@@ -2869,29 +2674,7 @@ class Client:
except Exception as e:
self.onMessageError(exception=e, msg=m)
def startListening(self):
"""Start listening from an external event loop.
Raises:
FBchatException: If request failed
"""
self.listening = True
def doOneListen(self, markAlive=None):
"""Do one cycle of the listening loop.
This method is useful if you want to control the client from an external event
loop.
Warning:
``markAlive`` parameter is deprecated, use :func:`Client.setActiveStatus`
or ``markAlive`` parameter in :func:`Client.listen` instead.
Returns:
bool: Whether the loop should keep running
"""
if markAlive is not None:
self._markAlive = markAlive
def _doOneListen(self):
try:
if self._markAlive:
self._ping()
@@ -2910,7 +2693,6 @@ class Client:
if e.request_status_code in [502, 503]:
# Bump pull channel, while contraining withing 0-4
self._pull_channel = (self._pull_channel + 1) % 5
self.startListening()
else:
raise e
except Exception as e:
@@ -2918,11 +2700,6 @@ class Client:
return True
def stopListening(self):
"""Clean up the variables from `Client.startListening`."""
self.listening = False
self._sticky, self._pool = (None, None)
def listen(self, markAlive=None):
"""Initialize and runs the listening loop continually.
@@ -2932,13 +2709,12 @@ class Client:
if markAlive is not None:
self.setActiveStatus(markAlive)
self.startListening()
self.onListening()
while self.listening and self.doOneListen():
while self._doOneListen():
pass
self.stopListening()
self._sticky, self._pool = (None, None)
def setActiveStatus(self, markAlive):
"""Change active status while listening.
@@ -2996,7 +2772,6 @@ class Client:
self,
mid=None,
author_id=None,
message=None,
message_object=None,
thread_id=None,
thread_type=ThreadType.USER,
@@ -3009,7 +2784,6 @@ class Client:
Args:
mid: The message ID
author_id: The ID of the author
message: (deprecated. Use ``message_object.text`` instead)
message_object (Message): The message (As a `Message` object)
thread_id: Thread ID that the message was sent to. See :ref:`intro_threads`
thread_type (ThreadType): Type of thread that the message was sent to. See :ref:`intro_threads`

View File

@@ -50,7 +50,3 @@ class FBchatPleaseRefresh(FBchatFacebookError):
fb_error_code = "1357004"
fb_error_message = "Please try closing and re-opening your browser window."
class FBchatUserError(FBchatException):
"""Thrown by ``fbchat`` when wrong values are entered."""

View File

@@ -103,16 +103,3 @@ class Group(Thread):
def _to_send_data(self):
return {"thread_fbid": self.uid}
@attr.s(cmp=False, init=False)
class Room(Group):
"""Deprecated. Use `Group` instead."""
# True is room is not discoverable
privacy_mode = attr.ib(None)
def __init__(self, uid, privacy_mode=None, **kwargs):
super(Room, self).__init__(uid, **kwargs)
self.type = ThreadType.ROOM
self.privacy_mode = privacy_mode

View File

@@ -23,11 +23,11 @@ def find_input_fields(html):
return bs4.BeautifulSoup(html, "html.parser", parse_only=bs4.SoupStrainer("input"))
def session_factory(user_agent=None):
def session_factory():
session = requests.session()
session.headers["Referer"] = "https://www.facebook.com"
# TODO: Deprecate setting the user agent manually
session.headers["User-Agent"] = user_agent or random.choice(_util.USER_AGENTS)
session.headers["User-Agent"] = random.choice(_util.USER_AGENTS)
return session
@@ -120,8 +120,8 @@ class State:
}
@classmethod
def login(cls, email, password, on_2fa_callback, user_agent=None):
session = session_factory(user_agent=user_agent)
def login(cls, email, password, on_2fa_callback):
session = session_factory()
soup = find_input_fields(session.get("https://m.facebook.com/").text)
data = dict(
@@ -147,7 +147,7 @@ class State:
if is_home(r.url):
return cls.from_session(session=session)
else:
raise _exception.FBchatUserError(
raise _exception.FBchatException(
"Login failed. Check email/password. "
"(Failed on url: {})".format(r.url)
)
@@ -201,63 +201,29 @@ class State:
return self._session.cookies.get_dict()
@classmethod
def from_cookies(cls, cookies, user_agent=None):
session = session_factory(user_agent=user_agent)
def from_cookies(cls, cookies):
session = session_factory()
session.cookies = requests.cookies.merge_cookies(session.cookies, cookies)
return cls.from_session(session=session)
def _do_refresh(self):
# TODO: Raise the error instead, and make the user do the refresh manually
# It may be a bad idea to do this in an exception handler, if you have a better method, please suggest it!
log.warning("Refreshing state and resending request")
new = State.from_session(session=self._session)
self.user_id = new.user_id
self._fb_dtsg = new._fb_dtsg
self._revision = new._revision
self._counter = new._counter
self._logout_h = new._logout_h or self._logout_h
def _get(self, url, params, error_retries=3):
params.update(self.get_params())
r = self._session.get(_util.prefix_url(url), params=params)
content = _util.check_request(r)
j = _util.to_json(content)
try:
_util.handle_payload_error(j)
except _exception.FBchatPleaseRefresh:
if error_retries > 0:
self._do_refresh()
return self._get(url, params, error_retries=error_retries - 1)
raise
return j
return _util.to_json(content)
def _post(self, url, data, files=None, as_graphql=False, error_retries=3):
def _post(self, url, data, files=None, as_graphql=False):
data.update(self.get_params())
r = self._session.post(_util.prefix_url(url), data=data, files=files)
content = _util.check_request(r)
try:
if as_graphql:
return _graphql.response_to_json(content)
else:
j = _util.to_json(content)
# TODO: Remove this, and move it to _payload_post instead
# We can't yet, since errors raised in here need to be caught below
_util.handle_payload_error(j)
return j
except _exception.FBchatPleaseRefresh:
if error_retries > 0:
self._do_refresh()
return self._post(
url,
data,
files=files,
as_graphql=as_graphql,
error_retries=error_retries - 1,
)
raise
return _util.to_json(content)
def _payload_post(self, url, data, files=None):
j = self._post(url, data, files=files)
_util.handle_payload_error(j)
try:
return j["payload"]
except (KeyError, TypeError):

View File

@@ -10,7 +10,6 @@ class ThreadType(Enum):
USER = 1
GROUP = 2
ROOM = 2
PAGE = 3
def _to_class(self):
@@ -20,7 +19,6 @@ class ThreadType(Enum):
return {
ThreadType.USER: _user.User,
ThreadType.GROUP: _group.Group,
ThreadType.ROOM: _group.Room,
ThreadType.PAGE: _page.Page,
}[self]

View File

@@ -45,9 +45,11 @@ def client2(pytestconfig):
@pytest.fixture(scope="module")
def client(client1, thread):
client1.setDefaultThread(thread["id"], thread["type"])
# TODO: These are commented out since `setDefaultThread` is removed - But now any
# test that use this fixture is likely broken!
# client1.setDefaultThread(thread["id"], thread["type"])
yield client1
client1.resetDefaultThread()
# client1.resetDefaultThread()
@pytest.fixture(scope="session", params=["client1", "client2"])

View File

@@ -3,7 +3,7 @@ import py_compile
from glob import glob
from os import path, environ
from fbchat import FBchatUserError, Message, Client
from fbchat import FBchatException, Message, Client
@pytest.mark.offline
@@ -24,7 +24,7 @@ def test_login(client1):
assert not client1.isLoggedIn()
with pytest.raises(FBchatUserError):
with pytest.raises(FBchatException):
client1.login("<invalid email>", "<invalid password>", max_tries=1)
client1.login(email, password)
@@ -38,13 +38,3 @@ def test_sessions(client1):
Client("no email needed", "no password needed", session_cookies=session)
client1.setSession(session)
assert client1.isLoggedIn()
@pytest.mark.tryfirst
def test_default_thread(client1, thread):
client1.setDefaultThread(thread["id"], thread["type"])
assert client1.send(Message(text="Sent to the specified thread"))
client1.resetDefaultThread()
with pytest.raises(ValueError):
client1.send(Message(text="Should not be sent"))

View File

@@ -39,22 +39,20 @@ TEXT_LIST = [
class ClientThread(threading.Thread):
# TODO: Refactor this to work with the new listening setup
def __init__(self, client, *args, **kwargs):
self.client = client
self.should_stop = threading.Event()
super(ClientThread, self).__init__(*args, **kwargs)
def start(self):
self.client.startListening()
self.client.doOneListen() # QPrimer, Facebook now knows we're about to start pulling
self.client._doOneListen() # QPrimer, Facebook now knows we're about to start pulling
super(ClientThread, self).start()
def run(self):
while not self.should_stop.is_set() and self.client.doOneListen():
while not self.should_stop.is_set() and self.client._doOneListen():
pass
self.client.stopListening()
class CaughtValue(threading.Event):
def set(self, res):
@@ -93,7 +91,6 @@ def load_client(n, cache):
client = Client(
load_variable("client{}_email".format(n), cache),
load_variable("client{}_password".format(n), cache),
user_agent="Mozilla/5.0 (Windows NT 6.3; WOW64; ; NCT50_AAP285C84A1328) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36",
session_cookies=cache.get("client{}_session".format(n), None),
max_tries=1,
)