Merge pull request #419 from carpedm20/remove-client-variables
Clean up `Client` variables
This commit is contained in:
@@ -37,13 +37,14 @@ class Client(object):
|
|||||||
"""Verify ssl certificate, set to False to allow debugging with a proxy"""
|
"""Verify ssl certificate, set to False to allow debugging with a proxy"""
|
||||||
listening = False
|
listening = False
|
||||||
"""Whether the client is listening. Used when creating an external event loop to determine when to stop listening"""
|
"""Whether the client is listening. Used when creating an external event loop to determine when to stop listening"""
|
||||||
uid = None
|
|
||||||
"""
|
|
||||||
The ID of the client.
|
|
||||||
Can be used as `thread_id`. See :ref:`intro_threads` for more info.
|
|
||||||
|
|
||||||
Note: Modifying this results in undefined behaviour
|
@property
|
||||||
|
def uid(self):
|
||||||
|
"""The ID of the client.
|
||||||
|
|
||||||
|
Can be used as `thread_id`. See :ref:`intro_threads` for more info.
|
||||||
"""
|
"""
|
||||||
|
return self._uid
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -67,15 +68,14 @@ class Client(object):
|
|||||||
:type logging_level: int
|
:type logging_level: int
|
||||||
:raises: FBchatException on failed login
|
:raises: FBchatException on failed login
|
||||||
"""
|
"""
|
||||||
self.sticky, self.pool = (None, None)
|
self._sticky, self._pool = (None, None)
|
||||||
self._session = requests.session()
|
self._session = requests.session()
|
||||||
self.req_counter = 1
|
self._req_counter = 1
|
||||||
self.seq = "0"
|
self._seq = "0"
|
||||||
# See `createPoll` for the reason for using `OrderedDict` here
|
# See `createPoll` for the reason for using `OrderedDict` here
|
||||||
self.payloadDefault = OrderedDict()
|
self._payload_default = OrderedDict()
|
||||||
self.client = "mercury"
|
self._default_thread_id = None
|
||||||
self.default_thread_id = None
|
self._default_thread_type = None
|
||||||
self.default_thread_type = None
|
|
||||||
self.req_url = ReqUrl()
|
self.req_url = ReqUrl()
|
||||||
self._markAlive = True
|
self._markAlive = True
|
||||||
self._buddylist = dict()
|
self._buddylist = dict()
|
||||||
@@ -100,9 +100,6 @@ class Client(object):
|
|||||||
or not self.isLoggedIn()
|
or not self.isLoggedIn()
|
||||||
):
|
):
|
||||||
self.login(email, password, max_tries)
|
self.login(email, password, max_tries)
|
||||||
else:
|
|
||||||
self.email = email
|
|
||||||
self.password = password
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
INTERNAL REQUEST METHODS
|
INTERNAL REQUEST METHODS
|
||||||
@@ -112,12 +109,12 @@ class Client(object):
|
|||||||
"""Adds the following defaults to the payload:
|
"""Adds the following defaults to the payload:
|
||||||
__rev, __user, __a, ttstamp, fb_dtsg, __req
|
__rev, __user, __a, ttstamp, fb_dtsg, __req
|
||||||
"""
|
"""
|
||||||
payload = self.payloadDefault.copy()
|
payload = self._payload_default.copy()
|
||||||
if query:
|
if query:
|
||||||
payload.update(query)
|
payload.update(query)
|
||||||
payload["__req"] = str_base(self.req_counter, 36)
|
payload["__req"] = str_base(self._req_counter, 36)
|
||||||
payload["seq"] = self.seq
|
payload["seq"] = self._seq
|
||||||
self.req_counter += 1
|
self._req_counter += 1
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
def _fix_fb_errors(self, error_code):
|
def _fix_fb_errors(self, error_code):
|
||||||
@@ -215,7 +212,7 @@ class Client(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _cleanPost(self, url, query=None, timeout=30):
|
def _cleanPost(self, url, query=None, timeout=30):
|
||||||
self.req_counter += 1
|
self._req_counter += 1
|
||||||
return self._session.post(
|
return self._session.post(
|
||||||
url,
|
url,
|
||||||
headers=self._header,
|
headers=self._header,
|
||||||
@@ -299,60 +296,55 @@ class Client(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def _resetValues(self):
|
def _resetValues(self):
|
||||||
self.payloadDefault = OrderedDict()
|
self._payload_default = OrderedDict()
|
||||||
self._session = requests.session()
|
self._session = requests.session()
|
||||||
self.req_counter = 1
|
self._req_counter = 1
|
||||||
self.seq = "0"
|
self._seq = "0"
|
||||||
self.uid = None
|
self._uid = None
|
||||||
|
|
||||||
def _postLogin(self):
|
def _postLogin(self):
|
||||||
self.payloadDefault = OrderedDict()
|
self._payload_default = OrderedDict()
|
||||||
self.client_id = hex(int(random() * 2147483648))[2:]
|
self._client_id = hex(int(random() * 2147483648))[2:]
|
||||||
self.start_time = now()
|
self._uid = self._session.cookies.get_dict().get("c_user")
|
||||||
self.uid = self._session.cookies.get_dict().get("c_user")
|
if self._uid is None:
|
||||||
if self.uid is None:
|
|
||||||
raise FBchatException("Could not find c_user cookie")
|
raise FBchatException("Could not find c_user cookie")
|
||||||
self.uid = str(self.uid)
|
self._uid = str(self._uid)
|
||||||
self.user_channel = "p_{}".format(self.uid)
|
|
||||||
self.ttstamp = ""
|
|
||||||
|
|
||||||
r = self._get(self.req_url.BASE)
|
r = self._get(self.req_url.BASE)
|
||||||
soup = bs(r.text, "html.parser")
|
soup = bs(r.text, "html.parser")
|
||||||
|
|
||||||
fb_dtsg_element = soup.find("input", {"name": "fb_dtsg"})
|
fb_dtsg_element = soup.find("input", {"name": "fb_dtsg"})
|
||||||
if fb_dtsg_element:
|
if fb_dtsg_element:
|
||||||
self.fb_dtsg = fb_dtsg_element["value"]
|
fb_dtsg = fb_dtsg_element["value"]
|
||||||
else:
|
else:
|
||||||
self.fb_dtsg = re.search(r'name="fb_dtsg" value="(.*?)"', r.text).group(1)
|
fb_dtsg = re.search(r'name="fb_dtsg" value="(.*?)"', r.text).group(1)
|
||||||
|
|
||||||
fb_h_element = soup.find("input", {"name": "h"})
|
fb_h_element = soup.find("input", {"name": "h"})
|
||||||
if fb_h_element:
|
if fb_h_element:
|
||||||
self.fb_h = fb_h_element["value"]
|
self._fb_h = fb_h_element["value"]
|
||||||
|
|
||||||
for i in self.fb_dtsg:
|
ttstamp = ""
|
||||||
self.ttstamp += str(ord(i))
|
for i in fb_dtsg:
|
||||||
self.ttstamp += "2"
|
ttstamp += str(ord(i))
|
||||||
|
ttstamp += "2"
|
||||||
# Set default payload
|
# Set default payload
|
||||||
self.payloadDefault["__rev"] = int(
|
self._payload_default["__rev"] = int(
|
||||||
r.text.split('"client_revision":', 1)[1].split(",", 1)[0]
|
r.text.split('"client_revision":', 1)[1].split(",", 1)[0]
|
||||||
)
|
)
|
||||||
self.payloadDefault["__user"] = self.uid
|
self._payload_default["__user"] = self._uid
|
||||||
self.payloadDefault["__a"] = "1"
|
self._payload_default["__a"] = "1"
|
||||||
self.payloadDefault["ttstamp"] = self.ttstamp
|
self._payload_default["ttstamp"] = ttstamp
|
||||||
self.payloadDefault["fb_dtsg"] = self.fb_dtsg
|
self._payload_default["fb_dtsg"] = fb_dtsg
|
||||||
|
|
||||||
def _login(self):
|
|
||||||
if not (self.email and self.password):
|
|
||||||
raise FBchatUserError("Email and password not found.")
|
|
||||||
|
|
||||||
|
def _login(self, email, password):
|
||||||
soup = bs(self._get(self.req_url.MOBILE).text, "html.parser")
|
soup = bs(self._get(self.req_url.MOBILE).text, "html.parser")
|
||||||
data = dict(
|
data = dict(
|
||||||
(elem["name"], elem["value"])
|
(elem["name"], elem["value"])
|
||||||
for elem in soup.findAll("input")
|
for elem in soup.findAll("input")
|
||||||
if elem.has_attr("value") and elem.has_attr("name")
|
if elem.has_attr("value") and elem.has_attr("name")
|
||||||
)
|
)
|
||||||
data["email"] = self.email
|
data["email"] = email
|
||||||
data["pass"] = self.password
|
data["pass"] = password
|
||||||
data["login"] = "Log In"
|
data["login"] = "Log In"
|
||||||
|
|
||||||
r = self._cleanPost(self.req_url.LOGIN, data)
|
r = self._cleanPost(self.req_url.LOGIN, data)
|
||||||
@@ -492,11 +484,8 @@ class Client(object):
|
|||||||
if not (email and password):
|
if not (email and password):
|
||||||
raise FBchatUserError("Email and password not set")
|
raise FBchatUserError("Email and password not set")
|
||||||
|
|
||||||
self.email = email
|
|
||||||
self.password = password
|
|
||||||
|
|
||||||
for i in range(1, max_tries + 1):
|
for i in range(1, max_tries + 1):
|
||||||
login_successful, login_url = self._login()
|
login_successful, login_url = self._login(email, password)
|
||||||
if not login_successful:
|
if not login_successful:
|
||||||
log.warning(
|
log.warning(
|
||||||
"Attempt #{} failed{}".format(
|
"Attempt #{} failed{}".format(
|
||||||
@@ -522,11 +511,11 @@ class Client(object):
|
|||||||
:return: True if the action was successful
|
:return: True if the action was successful
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
if not hasattr(self, "fb_h"):
|
if not hasattr(self, "_fb_h"):
|
||||||
h_r = self._post(self.req_url.MODERN_SETTINGS_MENU, {"pmid": "4"})
|
h_r = self._post(self.req_url.MODERN_SETTINGS_MENU, {"pmid": "4"})
|
||||||
self.fb_h = re.search(r'name=\\"h\\" value=\\"(.*?)\\"', h_r.text).group(1)
|
self._fb_h = re.search(r'name=\\"h\\" value=\\"(.*?)\\"', h_r.text).group(1)
|
||||||
|
|
||||||
data = {"ref": "mb", "h": self.fb_h}
|
data = {"ref": "mb", "h": self._fb_h}
|
||||||
|
|
||||||
r = self._get(self.req_url.LOGOUT, data)
|
r = self._get(self.req_url.LOGOUT, data)
|
||||||
|
|
||||||
@@ -551,8 +540,8 @@ class Client(object):
|
|||||||
:rtype: tuple
|
:rtype: tuple
|
||||||
"""
|
"""
|
||||||
if given_thread_id is None:
|
if given_thread_id is None:
|
||||||
if self.default_thread_id is not None:
|
if self._default_thread_id is not None:
|
||||||
return self.default_thread_id, self.default_thread_type
|
return self._default_thread_id, self._default_thread_type
|
||||||
else:
|
else:
|
||||||
raise ValueError("Thread ID is not set")
|
raise ValueError("Thread ID is not set")
|
||||||
else:
|
else:
|
||||||
@@ -566,8 +555,8 @@ class Client(object):
|
|||||||
:param thread_type: See :ref:`intro_threads`
|
:param thread_type: See :ref:`intro_threads`
|
||||||
:type thread_type: models.ThreadType
|
:type thread_type: models.ThreadType
|
||||||
"""
|
"""
|
||||||
self.default_thread_id = thread_id
|
self._default_thread_id = thread_id
|
||||||
self.default_thread_type = thread_type
|
self._default_thread_type = thread_type
|
||||||
|
|
||||||
def resetDefaultThread(self):
|
def resetDefaultThread(self):
|
||||||
"""Resets default thread"""
|
"""Resets default thread"""
|
||||||
@@ -672,7 +661,7 @@ class Client(object):
|
|||||||
:rtype: list
|
:rtype: list
|
||||||
:raises: FBchatException if request failed
|
:raises: FBchatException if request failed
|
||||||
"""
|
"""
|
||||||
data = {"viewer": self.uid}
|
data = {"viewer": self._uid}
|
||||||
j = self._post(
|
j = self._post(
|
||||||
self.req_url.ALL_USERS, query=data, fix_request=True, as_json=True
|
self.req_url.ALL_USERS, query=data, fix_request=True, as_json=True
|
||||||
)
|
)
|
||||||
@@ -1260,13 +1249,13 @@ class Client(object):
|
|||||||
messageAndOTID = generateOfflineThreadingID()
|
messageAndOTID = generateOfflineThreadingID()
|
||||||
timestamp = now()
|
timestamp = now()
|
||||||
data = {
|
data = {
|
||||||
"client": self.client,
|
"client": "mercury",
|
||||||
"author": "fbid:{}".format(self.uid),
|
"author": "fbid:{}".format(self._uid),
|
||||||
"timestamp": timestamp,
|
"timestamp": timestamp,
|
||||||
"source": "source:chat:web",
|
"source": "source:chat:web",
|
||||||
"offline_threading_id": messageAndOTID,
|
"offline_threading_id": messageAndOTID,
|
||||||
"message_id": messageAndOTID,
|
"message_id": messageAndOTID,
|
||||||
"threading_id": generateMessageID(self.client_id),
|
"threading_id": generateMessageID(self._client_id),
|
||||||
"ephemeral_ttl_mode:": "0",
|
"ephemeral_ttl_mode:": "0",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1331,7 +1320,7 @@ class Client(object):
|
|||||||
# update JS token if received in response
|
# update JS token if received in response
|
||||||
fb_dtsg = get_jsmods_require(j, 2)
|
fb_dtsg = get_jsmods_require(j, 2)
|
||||||
if fb_dtsg is not None:
|
if fb_dtsg is not None:
|
||||||
self.payloadDefault["fb_dtsg"] = fb_dtsg
|
self._payload_default["fb_dtsg"] = fb_dtsg
|
||||||
|
|
||||||
try:
|
try:
|
||||||
message_ids = [
|
message_ids = [
|
||||||
@@ -1738,7 +1727,7 @@ class Client(object):
|
|||||||
if len(user_ids) < 2:
|
if len(user_ids) < 2:
|
||||||
raise FBchatUserError("Error when creating group: Not enough participants")
|
raise FBchatUserError("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)
|
||||||
|
|
||||||
message_id, thread_id = self._doSendRequest(data, get_thread_id=True)
|
message_id, thread_id = self._doSendRequest(data, get_thread_id=True)
|
||||||
@@ -1766,7 +1755,7 @@ class Client(object):
|
|||||||
user_ids = require_list(user_ids)
|
user_ids = require_list(user_ids)
|
||||||
|
|
||||||
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 FBchatUserError(
|
||||||
"Error when adding users: Cannot add self to group thread"
|
"Error when adding users: Cannot add self to group thread"
|
||||||
)
|
)
|
||||||
@@ -1842,7 +1831,7 @@ class Client(object):
|
|||||||
|
|
||||||
data = {
|
data = {
|
||||||
"client_mutation_id": "0",
|
"client_mutation_id": "0",
|
||||||
"actor_id": self.uid,
|
"actor_id": self._uid,
|
||||||
"thread_fbid": thread_id,
|
"thread_fbid": thread_id,
|
||||||
"user_ids": user_ids,
|
"user_ids": user_ids,
|
||||||
"response": "ACCEPT" if approve else "DENY",
|
"response": "ACCEPT" if approve else "DENY",
|
||||||
@@ -2001,7 +1990,7 @@ class Client(object):
|
|||||||
data = {
|
data = {
|
||||||
"action": "ADD_REACTION" if reaction else "REMOVE_REACTION",
|
"action": "ADD_REACTION" if reaction else "REMOVE_REACTION",
|
||||||
"client_mutation_id": "1",
|
"client_mutation_id": "1",
|
||||||
"actor_id": self.uid,
|
"actor_id": self._uid,
|
||||||
"message_id": str(message_id),
|
"message_id": str(message_id),
|
||||||
"reaction": reaction.value if reaction else None,
|
"reaction": reaction.value if reaction else None,
|
||||||
}
|
}
|
||||||
@@ -2097,7 +2086,7 @@ class Client(object):
|
|||||||
|
|
||||||
# We're using ordered dicts, because the Facebook endpoint that parses the POST
|
# We're using ordered dicts, because the Facebook endpoint that parses the POST
|
||||||
# parameters is badly implemented, and deals with ordering the options wrongly.
|
# parameters is badly implemented, and deals with ordering the options wrongly.
|
||||||
# This also means we had to change `client.payloadDefault` to an ordered dict,
|
# This also means we had to change `client._payload_default` to an ordered dict,
|
||||||
# since that's being copied in between this point and the `requests` call
|
# since that's being copied in between this point and the `requests` call
|
||||||
#
|
#
|
||||||
# If you can find a way to fix this for the endpoint, or if you find another
|
# If you can find a way to fix this for the endpoint, or if you find another
|
||||||
@@ -2405,14 +2394,14 @@ class Client(object):
|
|||||||
|
|
||||||
def _ping(self):
|
def _ping(self):
|
||||||
data = {
|
data = {
|
||||||
"channel": self.user_channel,
|
"channel": "p_" + self._uid,
|
||||||
"clientid": self.client_id,
|
"clientid": self._client_id,
|
||||||
"partition": -2,
|
"partition": -2,
|
||||||
"cap": 0,
|
"cap": 0,
|
||||||
"uid": self.uid,
|
"uid": self._uid,
|
||||||
"sticky_token": self.sticky,
|
"sticky_token": self._sticky,
|
||||||
"sticky_pool": self.pool,
|
"sticky_pool": self._pool,
|
||||||
"viewer_uid": self.uid,
|
"viewer_uid": self._uid,
|
||||||
"state": "active",
|
"state": "active",
|
||||||
}
|
}
|
||||||
self._get(self.req_url.PING, data, fix_request=True, as_json=False)
|
self._get(self.req_url.PING, data, fix_request=True, as_json=False)
|
||||||
@@ -2421,9 +2410,9 @@ class Client(object):
|
|||||||
"""Call pull api with seq value to get message data."""
|
"""Call pull api with seq value to get message data."""
|
||||||
data = {
|
data = {
|
||||||
"msgs_recv": 0,
|
"msgs_recv": 0,
|
||||||
"sticky_token": self.sticky,
|
"sticky_token": self._sticky,
|
||||||
"sticky_pool": self.pool,
|
"sticky_pool": self._pool,
|
||||||
"clientid": self.client_id,
|
"clientid": self._client_id,
|
||||||
"state": "active" if self._markAlive else "offline",
|
"state": "active" if self._markAlive else "offline",
|
||||||
}
|
}
|
||||||
return self._get(self.req_url.STICKY, data, fix_request=True, as_json=True)
|
return self._get(self.req_url.STICKY, data, fix_request=True, as_json=True)
|
||||||
@@ -2976,11 +2965,11 @@ class Client(object):
|
|||||||
|
|
||||||
def _parseMessage(self, content):
|
def _parseMessage(self, content):
|
||||||
"""Get message and author name from content. May contain multiple messages in the content."""
|
"""Get message and author name from content. May contain multiple messages in the content."""
|
||||||
self.seq = content.get("seq", "0")
|
self._seq = content.get("seq", "0")
|
||||||
|
|
||||||
if "lb_info" in content:
|
if "lb_info" in content:
|
||||||
self.sticky = content["lb_info"]["sticky"]
|
self._sticky = content["lb_info"]["sticky"]
|
||||||
self.pool = content["lb_info"]["pool"]
|
self._pool = content["lb_info"]["pool"]
|
||||||
|
|
||||||
if "batches" in content:
|
if "batches" in content:
|
||||||
for batch in content["batches"]:
|
for batch in content["batches"]:
|
||||||
@@ -3013,7 +3002,7 @@ class Client(object):
|
|||||||
thread_id = str(thread_id)
|
thread_id = str(thread_id)
|
||||||
else:
|
else:
|
||||||
thread_type = ThreadType.USER
|
thread_type = ThreadType.USER
|
||||||
if author_id == self.uid:
|
if author_id == self._uid:
|
||||||
thread_id = m.get("to")
|
thread_id = m.get("to")
|
||||||
else:
|
else:
|
||||||
thread_id = author_id
|
thread_id = author_id
|
||||||
@@ -3126,7 +3115,7 @@ class Client(object):
|
|||||||
def stopListening(self):
|
def stopListening(self):
|
||||||
"""Cleans up the variables from startListening"""
|
"""Cleans up the variables from startListening"""
|
||||||
self.listening = False
|
self.listening = False
|
||||||
self.sticky, self.pool = (None, None)
|
self._sticky, self._pool = (None, None)
|
||||||
|
|
||||||
def listen(self, markAlive=None):
|
def listen(self, markAlive=None):
|
||||||
"""
|
"""
|
||||||
|
Reference in New Issue
Block a user