Merge pull request #466 from carpedm20/various-removals
Various removals
This commit is contained in:
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -21,8 +21,8 @@ Traceback (most recent call last):
|
|||||||
File "[site-packages]/fbchat/client.py", line 78, in __init__
|
File "[site-packages]/fbchat/client.py", line 78, in __init__
|
||||||
self.login(email, password, max_tries)
|
self.login(email, password, max_tries)
|
||||||
File "[site-packages]/fbchat/client.py", line 407, in login
|
File "[site-packages]/fbchat/client.py", line 407, in login
|
||||||
raise FBchatUserError('Login failed. Check email/password. (Failed on URL: {})'.format(login_url))
|
raise FBchatException('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)
|
fbchat.FBchatException: Login failed. Check email/password. (Failed on URL: https://m.facebook.com/login.php?login_attempt=1)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Environment information
|
## Environment information
|
||||||
|
@@ -38,7 +38,6 @@ Exceptions
|
|||||||
|
|
||||||
.. autoexception:: FBchatException()
|
.. autoexception:: FBchatException()
|
||||||
.. autoexception:: FBchatFacebookError()
|
.. autoexception:: FBchatFacebookError()
|
||||||
.. autoexception:: FBchatUserError()
|
|
||||||
|
|
||||||
Attachments
|
Attachments
|
||||||
-----------
|
-----------
|
||||||
|
@@ -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!
|
# The order of these is somewhat significant, e.g. User has to be imported after Thread!
|
||||||
from . import _core, _util
|
from . import _core, _util
|
||||||
from ._exception import FBchatException, FBchatFacebookError, FBchatUserError
|
from ._exception import FBchatException, FBchatFacebookError
|
||||||
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, Room
|
from ._group import Group
|
||||||
from ._page import Page
|
from ._page import Page
|
||||||
from ._message import EmojiSize, MessageReaction, Mention, Message
|
from ._message import EmojiSize, MessageReaction, Mention, Message
|
||||||
from ._attachment import Attachment, UnsentMessage, ShareAttachment
|
from ._attachment import Attachment, UnsentMessage, ShareAttachment
|
||||||
|
@@ -7,7 +7,7 @@ from collections import OrderedDict
|
|||||||
from ._core import log
|
from ._core import log
|
||||||
from . import _util, _graphql, _state
|
from . import _util, _graphql, _state
|
||||||
|
|
||||||
from ._exception import FBchatException, FBchatFacebookError, FBchatUserError
|
from ._exception import FBchatException, FBchatFacebookError
|
||||||
from ._thread import ThreadType, ThreadLocation, ThreadColor
|
from ._thread import ThreadType, ThreadLocation, ThreadColor
|
||||||
from ._user import TypingStatus, User, ActiveStatus
|
from ._user import TypingStatus, User, ActiveStatus
|
||||||
from ._group import Group
|
from ._group import Group
|
||||||
@@ -43,25 +43,6 @@ class Client:
|
|||||||
to provide custom event handling (mainly useful while listening).
|
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
|
@property
|
||||||
def uid(self):
|
def uid(self):
|
||||||
"""The ID of the client.
|
"""The ID of the client.
|
||||||
@@ -70,16 +51,12 @@ class Client:
|
|||||||
"""
|
"""
|
||||||
return self._uid
|
return self._uid
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, email, password, session_cookies=None):
|
||||||
self, email, password, user_agent=None, max_tries=5, session_cookies=None
|
|
||||||
):
|
|
||||||
"""Initialize and log in the client.
|
"""Initialize and log in the client.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
email: Facebook ``email``, ``id`` or ``phone number``
|
email: Facebook ``email``, ``id`` or ``phone number``
|
||||||
password: Facebook account password
|
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)
|
session_cookies (dict): Cookies from a previous session (Will default to login if these are invalid)
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
@@ -87,8 +64,6 @@ class Client:
|
|||||||
"""
|
"""
|
||||||
self._sticky, self._pool = (None, None)
|
self._sticky, self._pool = (None, None)
|
||||||
self._seq = "0"
|
self._seq = "0"
|
||||||
self._default_thread_id = None
|
|
||||||
self._default_thread_type = None
|
|
||||||
self._pull_channel = 0
|
self._pull_channel = 0
|
||||||
self._markAlive = True
|
self._markAlive = True
|
||||||
self._buddylist = dict()
|
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 session cookies aren't set, not properly loaded or gives us an invalid session, then do the login
|
||||||
if (
|
if (
|
||||||
not session_cookies
|
not session_cookies
|
||||||
or not self.setSession(session_cookies, user_agent=user_agent)
|
or not self.setSession(session_cookies)
|
||||||
or not self.isLoggedIn()
|
or not self.isLoggedIn()
|
||||||
):
|
):
|
||||||
self.login(email, password, max_tries, user_agent=user_agent)
|
self.login(email, password)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
INTERNAL REQUEST METHODS
|
INTERNAL REQUEST METHODS
|
||||||
@@ -160,7 +135,7 @@ class Client:
|
|||||||
"""
|
"""
|
||||||
return self._state.get_cookies()
|
return self._state.get_cookies()
|
||||||
|
|
||||||
def setSession(self, session_cookies, user_agent=None):
|
def setSession(self, session_cookies):
|
||||||
"""Load session cookies.
|
"""Load session cookies.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -171,16 +146,14 @@ class Client:
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Load cookies into current session
|
# Load cookies into current session
|
||||||
self._state = _state.State.from_cookies(
|
self._state = _state.State.from_cookies(session_cookies)
|
||||||
session_cookies, user_agent=user_agent
|
|
||||||
)
|
|
||||||
self._uid = self._state.user_id
|
self._uid = self._state.user_id
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.exception("Failed loading session")
|
log.exception("Failed loading session")
|
||||||
return False
|
return False
|
||||||
return True
|
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``.
|
"""Login the user, using ``email`` and ``password``.
|
||||||
|
|
||||||
If the user is already logged in, this will do a re-login.
|
If the user is already logged in, this will do a re-login.
|
||||||
@@ -188,36 +161,20 @@ class Client:
|
|||||||
Args:
|
Args:
|
||||||
email: Facebook ``email`` or ``id`` or ``phone number``
|
email: Facebook ``email`` or ``id`` or ``phone number``
|
||||||
password: Facebook account password
|
password: Facebook account password
|
||||||
max_tries (int): Maximum number of times to try logging in
|
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
FBchatException: On failed login
|
FBchatException: On failed login
|
||||||
"""
|
"""
|
||||||
self.onLoggingIn(email=email)
|
self.onLoggingIn(email=email)
|
||||||
|
|
||||||
if max_tries < 1:
|
|
||||||
raise FBchatUserError("Cannot login: max_tries should be at least one")
|
|
||||||
|
|
||||||
if not (email and password):
|
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):
|
self._state = _state.State.login(
|
||||||
try:
|
email, password, on_2fa_callback=self.on2FACode
|
||||||
self._state = _state.State.login(
|
)
|
||||||
email,
|
self._uid = self._state.user_id
|
||||||
password,
|
self.onLoggedIn(email=email)
|
||||||
on_2fa_callback=self.on2FACode,
|
|
||||||
user_agent=user_agent,
|
|
||||||
)
|
|
||||||
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):
|
def logout(self):
|
||||||
"""Safely log out the client.
|
"""Safely log out the client.
|
||||||
@@ -235,45 +192,6 @@ class Client:
|
|||||||
END LOGIN METHODS
|
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
|
FETCH METHODS
|
||||||
"""
|
"""
|
||||||
@@ -494,8 +412,6 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
FBchatException: If request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"query": query,
|
"query": query,
|
||||||
"snippetOffset": offset,
|
"snippetOffset": offset,
|
||||||
@@ -624,7 +540,7 @@ class Client:
|
|||||||
if thread.type == ThreadType.USER:
|
if thread.type == ThreadType.USER:
|
||||||
users[id_] = thread
|
users[id_] = thread
|
||||||
else:
|
else:
|
||||||
raise FBchatUserError("Thread {} was not a user".format(thread))
|
raise ValueError("Thread {} was not a user".format(thread))
|
||||||
|
|
||||||
return users
|
return users
|
||||||
|
|
||||||
@@ -649,7 +565,7 @@ class Client:
|
|||||||
if thread.type == ThreadType.PAGE:
|
if thread.type == ThreadType.PAGE:
|
||||||
pages[id_] = thread
|
pages[id_] = thread
|
||||||
else:
|
else:
|
||||||
raise FBchatUserError("Thread {} was not a page".format(thread))
|
raise ValueError("Thread {} was not a page".format(thread))
|
||||||
|
|
||||||
return pages
|
return pages
|
||||||
|
|
||||||
@@ -671,7 +587,7 @@ class Client:
|
|||||||
if thread.type == ThreadType.GROUP:
|
if thread.type == ThreadType.GROUP:
|
||||||
groups[id_] = thread
|
groups[id_] = thread
|
||||||
else:
|
else:
|
||||||
raise FBchatUserError("Thread {} was not a group".format(thread))
|
raise ValueError("Thread {} was not a group".format(thread))
|
||||||
|
|
||||||
return groups
|
return groups
|
||||||
|
|
||||||
@@ -756,8 +672,6 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
FBchatException: If request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
"id": thread_id,
|
"id": thread_id,
|
||||||
"message_limit": limit,
|
"message_limit": limit,
|
||||||
@@ -789,12 +703,11 @@ class Client:
|
|||||||
return messages
|
return messages
|
||||||
|
|
||||||
def fetchThreadList(
|
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.
|
"""Fetch the client's thread list.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
offset: Deprecated. Do not use!
|
|
||||||
limit (int): Max. number of threads to retrieve. Capped at 20
|
limit (int): Max. number of threads to retrieve. Capped at 20
|
||||||
thread_location (ThreadLocation): INBOX, PENDING, ARCHIVED or OTHER
|
thread_location (ThreadLocation): INBOX, PENDING, ARCHIVED or OTHER
|
||||||
before (datetime.datetime): The point from which to retrieve threads
|
before (datetime.datetime): The point from which to retrieve threads
|
||||||
@@ -805,20 +718,13 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
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:
|
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:
|
if thread_location in ThreadLocation:
|
||||||
loc_str = thread_location.value
|
loc_str = thread_location.value
|
||||||
else:
|
else:
|
||||||
raise FBchatUserError('"thread_location" must be a value of ThreadLocation')
|
raise TypeError('"thread_location" must be a value of ThreadLocation')
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
"limit": limit,
|
"limit": limit,
|
||||||
@@ -891,6 +797,7 @@ class Client:
|
|||||||
image_id = str(image_id)
|
image_id = str(image_id)
|
||||||
data = {"photo_id": str(image_id)}
|
data = {"photo_id": str(image_id)}
|
||||||
j = self._post("/mercury/attachments/photo/", data)
|
j = self._post("/mercury/attachments/photo/", data)
|
||||||
|
_util.handle_payload_error(j)
|
||||||
|
|
||||||
url = _util.get_jsmods_require(j, 3)
|
url = _util.get_jsmods_require(j, 3)
|
||||||
if url is None:
|
if url is None:
|
||||||
@@ -910,7 +817,6 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
FBchatException: If request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
|
||||||
message_info = self._forcedFetch(thread_id, mid).get("message")
|
message_info = self._forcedFetch(thread_id, mid).get("message")
|
||||||
return Message._from_graphql(message_info)
|
return Message._from_graphql(message_info)
|
||||||
|
|
||||||
@@ -995,7 +901,6 @@ class Client:
|
|||||||
Returns:
|
Returns:
|
||||||
typing.Iterable: :class:`ImageAttachment` or :class:`VideoAttachment`
|
typing.Iterable: :class:`ImageAttachment` or :class:`VideoAttachment`
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
|
||||||
data = {"id": thread_id, "first": 48}
|
data = {"id": thread_id, "first": 48}
|
||||||
thread_id = str(thread_id)
|
thread_id = str(thread_id)
|
||||||
j, = self.graphql_requests(_graphql.from_query_id("515216185516880", data))
|
j, = self.graphql_requests(_graphql.from_query_id("515216185516880", data))
|
||||||
@@ -1057,32 +962,11 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
FBchatException: If request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, thread_type)
|
|
||||||
thread = thread_type._to_class()(thread_id)
|
thread = thread_type._to_class()(thread_id)
|
||||||
data = thread._to_send_data()
|
data = thread._to_send_data()
|
||||||
data.update(message._to_send_data())
|
data.update(message._to_send_data())
|
||||||
return self._doSendRequest(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):
|
def wave(self, wave_first=True, thread_id=None, thread_type=None):
|
||||||
"""Wave hello to a thread.
|
"""Wave hello to a thread.
|
||||||
|
|
||||||
@@ -1097,7 +981,6 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
FBchatException: If request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, thread_type)
|
|
||||||
thread = thread_type._to_class()(thread_id)
|
thread = thread_type._to_class()(thread_id)
|
||||||
data = thread._to_send_data()
|
data = thread._to_send_data()
|
||||||
data["action_type"] = "ma-type:user-generated-message"
|
data["action_type"] = "ma-type:user-generated-message"
|
||||||
@@ -1131,7 +1014,7 @@ class Client:
|
|||||||
)
|
)
|
||||||
elif isinstance(quick_reply, QuickReplyLocation):
|
elif isinstance(quick_reply, QuickReplyLocation):
|
||||||
if not isinstance(payload, LocationAttachment):
|
if not isinstance(payload, LocationAttachment):
|
||||||
raise ValueError(
|
raise TypeError(
|
||||||
"Payload must be an instance of `fbchat.LocationAttachment`"
|
"Payload must be an instance of `fbchat.LocationAttachment`"
|
||||||
)
|
)
|
||||||
return self.sendLocation(
|
return self.sendLocation(
|
||||||
@@ -1162,7 +1045,6 @@ class Client:
|
|||||||
def _sendLocation(
|
def _sendLocation(
|
||||||
self, location, current=True, message=None, thread_id=None, thread_type=None
|
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)
|
thread = thread_type._to_class()(thread_id)
|
||||||
data = thread._to_send_data()
|
data = thread._to_send_data()
|
||||||
if message is not None:
|
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.
|
`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)
|
thread = thread_type._to_class()(thread_id)
|
||||||
data = thread._to_send_data()
|
data = thread._to_send_data()
|
||||||
data.update(self._oldMessage(message)._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
|
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):
|
def forwardAttachment(self, attachment_id, thread_id=None):
|
||||||
"""Forward an attachment.
|
"""Forward an attachment.
|
||||||
|
|
||||||
@@ -1389,7 +1228,6 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
FBchatException: If request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
|
||||||
data = {
|
data = {
|
||||||
"attachment_id": attachment_id,
|
"attachment_id": attachment_id,
|
||||||
"recipient_map[{}]".format(_util.generateOfflineThreadingID()): thread_id,
|
"recipient_map[{}]".format(_util.generateOfflineThreadingID()): thread_id,
|
||||||
@@ -1417,7 +1255,7 @@ class Client:
|
|||||||
data = self._oldMessage(message)._to_send_data()
|
data = self._oldMessage(message)._to_send_data()
|
||||||
|
|
||||||
if len(user_ids) < 2:
|
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]):
|
for i, user_id in enumerate(user_ids + [self._uid]):
|
||||||
data["specific_to_list[{}]".format(i)] = "fbid:{}".format(user_id)
|
data["specific_to_list[{}]".format(i)] = "fbid:{}".format(user_id)
|
||||||
@@ -1439,7 +1277,6 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
FBchatException: If request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
|
||||||
data = Group(thread_id)._to_send_data()
|
data = Group(thread_id)._to_send_data()
|
||||||
|
|
||||||
data["action_type"] = "ma-type:log-message"
|
data["action_type"] = "ma-type:log-message"
|
||||||
@@ -1449,7 +1286,7 @@ class Client:
|
|||||||
|
|
||||||
for i, user_id in enumerate(user_ids):
|
for i, user_id in enumerate(user_ids):
|
||||||
if user_id == self._uid:
|
if user_id == self._uid:
|
||||||
raise FBchatUserError(
|
raise ValueError(
|
||||||
"Error when adding users: Cannot add self to group thread"
|
"Error when adding users: Cannot add self to group thread"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -1469,14 +1306,10 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
FBchatException: If request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
|
||||||
|
|
||||||
data = {"uid": user_id, "tid": thread_id}
|
data = {"uid": user_id, "tid": thread_id}
|
||||||
j = self._payload_post("/chat/remove_participants/", data)
|
j = self._payload_post("/chat/remove_participants/", data)
|
||||||
|
|
||||||
def _adminStatus(self, admin_ids, admin, thread_id=None):
|
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}
|
data = {"add": admin, "thread_fbid": thread_id}
|
||||||
|
|
||||||
admin_ids = _util.require_list(admin_ids)
|
admin_ids = _util.require_list(admin_ids)
|
||||||
@@ -1520,14 +1353,10 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
FBchatException: If request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
|
||||||
|
|
||||||
data = {"set_mode": int(require_admin_approval), "thread_fbid": thread_id}
|
data = {"set_mode": int(require_admin_approval), "thread_fbid": thread_id}
|
||||||
j = self._payload_post("/messaging/set_approval_mode/?dpr=1", data)
|
j = self._payload_post("/messaging/set_approval_mode/?dpr=1", data)
|
||||||
|
|
||||||
def _usersApproval(self, user_ids, approve, thread_id=None):
|
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)
|
user_ids = _util.require_list(user_ids)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
@@ -1576,8 +1405,6 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
FBchatException: If request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
|
||||||
|
|
||||||
data = {"thread_image_id": image_id, "thread_id": thread_id}
|
data = {"thread_image_id": image_id, "thread_id": thread_id}
|
||||||
|
|
||||||
j = self._payload_post("/messaging/set_thread_image/?dpr=1", data)
|
j = self._payload_post("/messaging/set_thread_image/?dpr=1", data)
|
||||||
@@ -1625,8 +1452,6 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
FBchatException: If request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, thread_type)
|
|
||||||
|
|
||||||
if thread_type == ThreadType.USER:
|
if thread_type == ThreadType.USER:
|
||||||
# The thread is a user, so we change the user's nickname
|
# The thread is a user, so we change the user's nickname
|
||||||
return self.changeNickname(
|
return self.changeNickname(
|
||||||
@@ -1650,8 +1475,6 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
FBchatException: If request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, thread_type)
|
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"nickname": nickname,
|
"nickname": nickname,
|
||||||
"participant_id": user_id,
|
"participant_id": user_id,
|
||||||
@@ -1671,8 +1494,6 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
FBchatException: If request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"color_choice": color.value if color != ThreadColor.MESSENGER_BLUE else "",
|
"color_choice": color.value if color != ThreadColor.MESSENGER_BLUE else "",
|
||||||
"thread_or_other_fbid": thread_id,
|
"thread_or_other_fbid": thread_id,
|
||||||
@@ -1695,8 +1516,6 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
FBchatException: If request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
|
||||||
|
|
||||||
data = {"emoji_choice": emoji, "thread_or_other_fbid": thread_id}
|
data = {"emoji_choice": emoji, "thread_or_other_fbid": thread_id}
|
||||||
j = self._payload_post(
|
j = self._payload_post(
|
||||||
"/messaging/save_thread_emoji/?source=thread_settings&dpr=1", data
|
"/messaging/save_thread_emoji/?source=thread_settings&dpr=1", data
|
||||||
@@ -1733,8 +1552,6 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
FBchatException: If request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"event_type": "EVENT",
|
"event_type": "EVENT",
|
||||||
"event_time": _util.datetime_to_seconds(plan.time),
|
"event_time": _util.datetime_to_seconds(plan.time),
|
||||||
@@ -1801,11 +1618,6 @@ class Client:
|
|||||||
}
|
}
|
||||||
j = self._payload_post("/ajax/eventreminder/rsvp", data)
|
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):
|
def createPoll(self, poll, thread_id=None):
|
||||||
"""Create poll in a group thread.
|
"""Create poll in a group thread.
|
||||||
|
|
||||||
@@ -1816,8 +1628,6 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
FBchatException: If request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
|
||||||
|
|
||||||
# We're using ordered dictionaries, because the Facebook endpoint that parses
|
# We're using ordered dictionaries, because the Facebook endpoint that parses
|
||||||
# the POST parameters is badly implemented, and deals with ordering the options
|
# 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
|
# wrongly. If you can find a way to fix this for the endpoint, or if you find
|
||||||
@@ -1874,8 +1684,6 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
FBchatException: If request failed
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, thread_type)
|
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"typ": status.value,
|
"typ": status.value,
|
||||||
"thread": thread_id,
|
"thread": thread_id,
|
||||||
@@ -2087,7 +1895,6 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
FBchatException: If request failed
|
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})
|
j = self._payload_post("/ajax/mercury/mark_spam.php?dpr=1", {"id": thread_id})
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -2117,7 +1924,6 @@ class Client:
|
|||||||
mute_time (datetime.timedelta): Time to mute, use ``None`` to mute forever
|
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: User/Group ID to mute. See :ref:`intro_threads`
|
||||||
"""
|
"""
|
||||||
thread_id, thread_type = self._getThread(thread_id, None)
|
|
||||||
if mute_time is None:
|
if mute_time is None:
|
||||||
mute_settings = -1
|
mute_settings = -1
|
||||||
else:
|
else:
|
||||||
@@ -2140,7 +1946,6 @@ class Client:
|
|||||||
mute: Boolean. True to mute, False to unmute
|
mute: Boolean. True to mute, False to unmute
|
||||||
thread_id: User/Group ID to mute. See :ref:`intro_threads`
|
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}
|
data = {"reactions_mute_mode": int(mute), "thread_fbid": thread_id}
|
||||||
j = self._payload_post(
|
j = self._payload_post(
|
||||||
"/ajax/mercury/change_reactions_mute_thread/?dpr=1", data
|
"/ajax/mercury/change_reactions_mute_thread/?dpr=1", data
|
||||||
@@ -2161,7 +1966,6 @@ class Client:
|
|||||||
mute: Boolean. True to mute, False to unmute
|
mute: Boolean. True to mute, False to unmute
|
||||||
thread_id: User/Group ID to mute. See :ref:`intro_threads`
|
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}
|
data = {"mentions_mute_mode": int(mute), "thread_fbid": thread_id}
|
||||||
j = self._payload_post("/ajax/mercury/change_mentions_mute_thread/?dpr=1", data)
|
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),
|
"https://{}-edge-chat.facebook.com/active_ping".format(self._pull_channel),
|
||||||
data,
|
data,
|
||||||
)
|
)
|
||||||
|
_util.handle_payload_error(j)
|
||||||
|
|
||||||
def _pullMessage(self):
|
def _pullMessage(self):
|
||||||
"""Call pull api to fetch message data."""
|
"""Call pull api to fetch message data."""
|
||||||
@@ -2205,9 +2010,11 @@ class Client:
|
|||||||
"clientid": self._state._client_id,
|
"clientid": self._state._client_id,
|
||||||
"state": "active" if self._markAlive else "offline",
|
"state": "active" if self._markAlive else "offline",
|
||||||
}
|
}
|
||||||
return self._get(
|
j = self._get(
|
||||||
"https://{}-edge-chat.facebook.com/pull".format(self._pull_channel), data
|
"https://{}-edge-chat.facebook.com/pull".format(self._pull_channel), data
|
||||||
)
|
)
|
||||||
|
_util.handle_payload_error(j)
|
||||||
|
return j
|
||||||
|
|
||||||
def _parseDelta(self, m):
|
def _parseDelta(self, m):
|
||||||
def getThreadIdAndThreadType(msg_metadata):
|
def getThreadIdAndThreadType(msg_metadata):
|
||||||
@@ -2728,7 +2535,6 @@ class Client:
|
|||||||
self.onMessage(
|
self.onMessage(
|
||||||
mid=message.uid,
|
mid=message.uid,
|
||||||
author_id=message.author,
|
author_id=message.author,
|
||||||
message=message.text,
|
|
||||||
message_object=message,
|
message_object=message,
|
||||||
thread_id=thread_id,
|
thread_id=thread_id,
|
||||||
thread_type=thread_type,
|
thread_type=thread_type,
|
||||||
@@ -2743,7 +2549,6 @@ class Client:
|
|||||||
self.onMessage(
|
self.onMessage(
|
||||||
mid=mid,
|
mid=mid,
|
||||||
author_id=author_id,
|
author_id=author_id,
|
||||||
message=delta.get("body", ""),
|
|
||||||
message_object=Message._from_pull(
|
message_object=Message._from_pull(
|
||||||
delta,
|
delta,
|
||||||
mid=mid,
|
mid=mid,
|
||||||
@@ -2869,29 +2674,7 @@ class Client:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.onMessageError(exception=e, msg=m)
|
self.onMessageError(exception=e, msg=m)
|
||||||
|
|
||||||
def startListening(self):
|
def _doOneListen(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
|
|
||||||
try:
|
try:
|
||||||
if self._markAlive:
|
if self._markAlive:
|
||||||
self._ping()
|
self._ping()
|
||||||
@@ -2910,7 +2693,6 @@ class Client:
|
|||||||
if e.request_status_code in [502, 503]:
|
if e.request_status_code in [502, 503]:
|
||||||
# Bump pull channel, while contraining withing 0-4
|
# Bump pull channel, while contraining withing 0-4
|
||||||
self._pull_channel = (self._pull_channel + 1) % 5
|
self._pull_channel = (self._pull_channel + 1) % 5
|
||||||
self.startListening()
|
|
||||||
else:
|
else:
|
||||||
raise e
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -2918,11 +2700,6 @@ class Client:
|
|||||||
|
|
||||||
return True
|
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):
|
def listen(self, markAlive=None):
|
||||||
"""Initialize and runs the listening loop continually.
|
"""Initialize and runs the listening loop continually.
|
||||||
|
|
||||||
@@ -2932,13 +2709,12 @@ class Client:
|
|||||||
if markAlive is not None:
|
if markAlive is not None:
|
||||||
self.setActiveStatus(markAlive)
|
self.setActiveStatus(markAlive)
|
||||||
|
|
||||||
self.startListening()
|
|
||||||
self.onListening()
|
self.onListening()
|
||||||
|
|
||||||
while self.listening and self.doOneListen():
|
while self._doOneListen():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.stopListening()
|
self._sticky, self._pool = (None, None)
|
||||||
|
|
||||||
def setActiveStatus(self, markAlive):
|
def setActiveStatus(self, markAlive):
|
||||||
"""Change active status while listening.
|
"""Change active status while listening.
|
||||||
@@ -2996,7 +2772,6 @@ class Client:
|
|||||||
self,
|
self,
|
||||||
mid=None,
|
mid=None,
|
||||||
author_id=None,
|
author_id=None,
|
||||||
message=None,
|
|
||||||
message_object=None,
|
message_object=None,
|
||||||
thread_id=None,
|
thread_id=None,
|
||||||
thread_type=ThreadType.USER,
|
thread_type=ThreadType.USER,
|
||||||
@@ -3009,7 +2784,6 @@ class Client:
|
|||||||
Args:
|
Args:
|
||||||
mid: The message ID
|
mid: The message ID
|
||||||
author_id: The ID of the author
|
author_id: The ID of the author
|
||||||
message: (deprecated. Use ``message_object.text`` instead)
|
|
||||||
message_object (Message): The message (As a `Message` object)
|
message_object (Message): The message (As a `Message` object)
|
||||||
thread_id: Thread ID that the message was sent to. See :ref:`intro_threads`
|
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`
|
thread_type (ThreadType): Type of thread that the message was sent to. See :ref:`intro_threads`
|
||||||
|
@@ -50,7 +50,3 @@ class FBchatPleaseRefresh(FBchatFacebookError):
|
|||||||
|
|
||||||
fb_error_code = "1357004"
|
fb_error_code = "1357004"
|
||||||
fb_error_message = "Please try closing and re-opening your browser window."
|
fb_error_message = "Please try closing and re-opening your browser window."
|
||||||
|
|
||||||
|
|
||||||
class FBchatUserError(FBchatException):
|
|
||||||
"""Thrown by ``fbchat`` when wrong values are entered."""
|
|
||||||
|
@@ -103,16 +103,3 @@ class Group(Thread):
|
|||||||
|
|
||||||
def _to_send_data(self):
|
def _to_send_data(self):
|
||||||
return {"thread_fbid": self.uid}
|
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
|
|
||||||
|
@@ -23,11 +23,11 @@ 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"))
|
||||||
|
|
||||||
|
|
||||||
def session_factory(user_agent=None):
|
def session_factory():
|
||||||
session = requests.session()
|
session = requests.session()
|
||||||
session.headers["Referer"] = "https://www.facebook.com"
|
session.headers["Referer"] = "https://www.facebook.com"
|
||||||
# TODO: Deprecate setting the user agent manually
|
# 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
|
return session
|
||||||
|
|
||||||
|
|
||||||
@@ -120,8 +120,8 @@ class State:
|
|||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def login(cls, email, password, on_2fa_callback, user_agent=None):
|
def login(cls, email, password, on_2fa_callback):
|
||||||
session = session_factory(user_agent=user_agent)
|
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)
|
||||||
data = dict(
|
data = dict(
|
||||||
@@ -147,7 +147,7 @@ class State:
|
|||||||
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.FBchatUserError(
|
raise _exception.FBchatException(
|
||||||
"Login failed. Check email/password. "
|
"Login failed. Check email/password. "
|
||||||
"(Failed on url: {})".format(r.url)
|
"(Failed on url: {})".format(r.url)
|
||||||
)
|
)
|
||||||
@@ -201,63 +201,29 @@ class State:
|
|||||||
return self._session.cookies.get_dict()
|
return self._session.cookies.get_dict()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_cookies(cls, cookies, user_agent=None):
|
def from_cookies(cls, cookies):
|
||||||
session = session_factory(user_agent=user_agent)
|
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 _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):
|
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)
|
||||||
j = _util.to_json(content)
|
return _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
|
|
||||||
|
|
||||||
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())
|
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)
|
||||||
try:
|
if as_graphql:
|
||||||
if as_graphql:
|
return _graphql.response_to_json(content)
|
||||||
return _graphql.response_to_json(content)
|
else:
|
||||||
else:
|
return _util.to_json(content)
|
||||||
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
|
|
||||||
|
|
||||||
def _payload_post(self, url, data, files=None):
|
def _payload_post(self, url, data, files=None):
|
||||||
j = self._post(url, data, files=files)
|
j = self._post(url, data, files=files)
|
||||||
|
_util.handle_payload_error(j)
|
||||||
try:
|
try:
|
||||||
return j["payload"]
|
return j["payload"]
|
||||||
except (KeyError, TypeError):
|
except (KeyError, TypeError):
|
||||||
|
@@ -10,7 +10,6 @@ class ThreadType(Enum):
|
|||||||
|
|
||||||
USER = 1
|
USER = 1
|
||||||
GROUP = 2
|
GROUP = 2
|
||||||
ROOM = 2
|
|
||||||
PAGE = 3
|
PAGE = 3
|
||||||
|
|
||||||
def _to_class(self):
|
def _to_class(self):
|
||||||
@@ -20,7 +19,6 @@ class ThreadType(Enum):
|
|||||||
return {
|
return {
|
||||||
ThreadType.USER: _user.User,
|
ThreadType.USER: _user.User,
|
||||||
ThreadType.GROUP: _group.Group,
|
ThreadType.GROUP: _group.Group,
|
||||||
ThreadType.ROOM: _group.Room,
|
|
||||||
ThreadType.PAGE: _page.Page,
|
ThreadType.PAGE: _page.Page,
|
||||||
}[self]
|
}[self]
|
||||||
|
|
||||||
|
@@ -45,9 +45,11 @@ def client2(pytestconfig):
|
|||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def client(client1, thread):
|
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
|
yield client1
|
||||||
client1.resetDefaultThread()
|
# client1.resetDefaultThread()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session", params=["client1", "client2"])
|
@pytest.fixture(scope="session", params=["client1", "client2"])
|
||||||
|
@@ -3,7 +3,7 @@ import py_compile
|
|||||||
|
|
||||||
from glob import glob
|
from glob import glob
|
||||||
from os import path, environ
|
from os import path, environ
|
||||||
from fbchat import FBchatUserError, Message, Client
|
from fbchat import FBchatException, Message, Client
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.offline
|
@pytest.mark.offline
|
||||||
@@ -24,7 +24,7 @@ def test_login(client1):
|
|||||||
|
|
||||||
assert not client1.isLoggedIn()
|
assert not client1.isLoggedIn()
|
||||||
|
|
||||||
with pytest.raises(FBchatUserError):
|
with pytest.raises(FBchatException):
|
||||||
client1.login("<invalid email>", "<invalid password>", max_tries=1)
|
client1.login("<invalid email>", "<invalid password>", max_tries=1)
|
||||||
|
|
||||||
client1.login(email, password)
|
client1.login(email, password)
|
||||||
@@ -38,13 +38,3 @@ def test_sessions(client1):
|
|||||||
Client("no email needed", "no password needed", session_cookies=session)
|
Client("no email needed", "no password needed", session_cookies=session)
|
||||||
client1.setSession(session)
|
client1.setSession(session)
|
||||||
assert client1.isLoggedIn()
|
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"))
|
|
||||||
|
@@ -39,22 +39,20 @@ TEXT_LIST = [
|
|||||||
|
|
||||||
|
|
||||||
class ClientThread(threading.Thread):
|
class ClientThread(threading.Thread):
|
||||||
|
# TODO: Refactor this to work with the new listening setup
|
||||||
def __init__(self, client, *args, **kwargs):
|
def __init__(self, client, *args, **kwargs):
|
||||||
self.client = client
|
self.client = client
|
||||||
self.should_stop = threading.Event()
|
self.should_stop = threading.Event()
|
||||||
super(ClientThread, self).__init__(*args, **kwargs)
|
super(ClientThread, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def start(self):
|
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()
|
super(ClientThread, self).start()
|
||||||
|
|
||||||
def run(self):
|
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
|
pass
|
||||||
|
|
||||||
self.client.stopListening()
|
|
||||||
|
|
||||||
|
|
||||||
class CaughtValue(threading.Event):
|
class CaughtValue(threading.Event):
|
||||||
def set(self, res):
|
def set(self, res):
|
||||||
@@ -93,7 +91,6 @@ def load_client(n, cache):
|
|||||||
client = Client(
|
client = Client(
|
||||||
load_variable("client{}_email".format(n), cache),
|
load_variable("client{}_email".format(n), cache),
|
||||||
load_variable("client{}_password".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),
|
session_cookies=cache.get("client{}_session".format(n), None),
|
||||||
max_tries=1,
|
max_tries=1,
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user