@@ -37,13 +37,14 @@ class Client(object):
3737 """Verify ssl certificate, set to False to allow debugging with a proxy"""
3838 listening = False
3939 """Whether the client is listening. Used when creating an external event loop to determine when to stop listening"""
40- uid = None
41- """
42- The ID of the client.
43- Can be used as `thread_id`. See :ref:`intro_threads` for more info.
4440
45- Note: Modifying this results in undefined behaviour
46- """
41+ @property
42+ def uid (self ):
43+ """The ID of the client.
44+
45+ Can be used as `thread_id`. See :ref:`intro_threads` for more info.
46+ """
47+ return self ._uid
4748
4849 def __init__ (
4950 self ,
@@ -67,15 +68,14 @@ def __init__(
6768 :type logging_level: int
6869 :raises: FBchatException on failed login
6970 """
70- self .sticky , self .pool = (None , None )
71+ self ._sticky , self ._pool = (None , None )
7172 self ._session = requests .session ()
72- self .req_counter = 1
73- self .seq = "0"
73+ self ._req_counter = 1
74+ self ._seq = "0"
7475 # See `createPoll` for the reason for using `OrderedDict` here
75- self .payloadDefault = OrderedDict ()
76- self .client = "mercury"
77- self .default_thread_id = None
78- self .default_thread_type = None
76+ self ._payload_default = OrderedDict ()
77+ self ._default_thread_id = None
78+ self ._default_thread_type = None
7979 self .req_url = ReqUrl ()
8080 self ._markAlive = True
8181 self ._buddylist = dict ()
@@ -100,9 +100,6 @@ def __init__(
100100 or not self .isLoggedIn ()
101101 ):
102102 self .login (email , password , max_tries )
103- else :
104- self .email = email
105- self .password = password
106103
107104 """
108105 INTERNAL REQUEST METHODS
@@ -112,12 +109,12 @@ def _generatePayload(self, query):
112109 """Adds the following defaults to the payload:
113110 __rev, __user, __a, ttstamp, fb_dtsg, __req
114111 """
115- payload = self .payloadDefault .copy ()
112+ payload = self ._payload_default .copy ()
116113 if query :
117114 payload .update (query )
118- payload ["__req" ] = str_base (self .req_counter , 36 )
119- payload ["seq" ] = self .seq
120- self .req_counter += 1
115+ payload ["__req" ] = str_base (self ._req_counter , 36 )
116+ payload ["seq" ] = self ._seq
117+ self ._req_counter += 1
121118 return payload
122119
123120 def _fix_fb_errors (self , error_code ):
@@ -215,7 +212,7 @@ def _cleanGet(self, url, query=None, timeout=30, allow_redirects=True):
215212 )
216213
217214 def _cleanPost (self , url , query = None , timeout = 30 ):
218- self .req_counter += 1
215+ self ._req_counter += 1
219216 return self ._session .post (
220217 url ,
221218 headers = self ._header ,
@@ -299,60 +296,55 @@ def graphql_request(self, query):
299296 """
300297
301298 def _resetValues (self ):
302- self .payloadDefault = OrderedDict ()
299+ self ._payload_default = OrderedDict ()
303300 self ._session = requests .session ()
304- self .req_counter = 1
305- self .seq = "0"
306- self .uid = None
301+ self ._req_counter = 1
302+ self ._seq = "0"
303+ self ._uid = None
307304
308305 def _postLogin (self ):
309- self .payloadDefault = OrderedDict ()
310- self .client_id = hex (int (random () * 2147483648 ))[2 :]
311- self .start_time = now ()
312- self .uid = self ._session .cookies .get_dict ().get ("c_user" )
313- if self .uid is None :
306+ self ._payload_default = OrderedDict ()
307+ self ._client_id = hex (int (random () * 2147483648 ))[2 :]
308+ self ._uid = self ._session .cookies .get_dict ().get ("c_user" )
309+ if self ._uid is None :
314310 raise FBchatException ("Could not find c_user cookie" )
315- self .uid = str (self .uid )
316- self .user_channel = "p_{}" .format (self .uid )
317- self .ttstamp = ""
311+ self ._uid = str (self ._uid )
318312
319313 r = self ._get (self .req_url .BASE )
320314 soup = bs (r .text , "html.parser" )
321315
322316 fb_dtsg_element = soup .find ("input" , {"name" : "fb_dtsg" })
323317 if fb_dtsg_element :
324- self . fb_dtsg = fb_dtsg_element ["value" ]
318+ fb_dtsg = fb_dtsg_element ["value" ]
325319 else :
326- self . fb_dtsg = re .search (r'name="fb_dtsg" value="(.*?)"' , r .text ).group (1 )
320+ fb_dtsg = re .search (r'name="fb_dtsg" value="(.*?)"' , r .text ).group (1 )
327321
328322 fb_h_element = soup .find ("input" , {"name" : "h" })
329323 if fb_h_element :
330- self .fb_h = fb_h_element ["value" ]
324+ self ._fb_h = fb_h_element ["value" ]
331325
332- for i in self .fb_dtsg :
333- self .ttstamp += str (ord (i ))
334- self .ttstamp += "2"
326+ ttstamp = ""
327+ for i in fb_dtsg :
328+ ttstamp += str (ord (i ))
329+ ttstamp += "2"
335330 # Set default payload
336- self .payloadDefault ["__rev" ] = int (
331+ self ._payload_default ["__rev" ] = int (
337332 r .text .split ('"client_revision":' , 1 )[1 ].split ("," , 1 )[0 ]
338333 )
339- self .payloadDefault ["__user" ] = self .uid
340- self .payloadDefault ["__a" ] = "1"
341- self .payloadDefault ["ttstamp" ] = self .ttstamp
342- self .payloadDefault ["fb_dtsg" ] = self .fb_dtsg
343-
344- def _login (self ):
345- if not (self .email and self .password ):
346- raise FBchatUserError ("Email and password not found." )
334+ self ._payload_default ["__user" ] = self ._uid
335+ self ._payload_default ["__a" ] = "1"
336+ self ._payload_default ["ttstamp" ] = ttstamp
337+ self ._payload_default ["fb_dtsg" ] = fb_dtsg
347338
339+ def _login (self , email , password ):
348340 soup = bs (self ._get (self .req_url .MOBILE ).text , "html.parser" )
349341 data = dict (
350342 (elem ["name" ], elem ["value" ])
351343 for elem in soup .findAll ("input" )
352344 if elem .has_attr ("value" ) and elem .has_attr ("name" )
353345 )
354- data ["email" ] = self . email
355- data ["pass" ] = self . password
346+ data ["email" ] = email
347+ data ["pass" ] = password
356348 data ["login" ] = "Log In"
357349
358350 r = self ._cleanPost (self .req_url .LOGIN , data )
@@ -492,11 +484,8 @@ def login(self, email, password, max_tries=5):
492484 if not (email and password ):
493485 raise FBchatUserError ("Email and password not set" )
494486
495- self .email = email
496- self .password = password
497-
498487 for i in range (1 , max_tries + 1 ):
499- login_successful , login_url = self ._login ()
488+ login_successful , login_url = self ._login (email , password )
500489 if not login_successful :
501490 log .warning (
502491 "Attempt #{} failed{}" .format (
@@ -522,11 +511,11 @@ def logout(self):
522511 :return: True if the action was successful
523512 :rtype: bool
524513 """
525- if not hasattr (self , "fb_h " ):
514+ if not hasattr (self , "_fb_h " ):
526515 h_r = self ._post (self .req_url .MODERN_SETTINGS_MENU , {"pmid" : "4" })
527- self .fb_h = re .search (r'name=\\"h\\" value=\\"(.*?)\\"' , h_r .text ).group (1 )
516+ self ._fb_h = re .search (r'name=\\"h\\" value=\\"(.*?)\\"' , h_r .text ).group (1 )
528517
529- data = {"ref" : "mb" , "h" : self .fb_h }
518+ data = {"ref" : "mb" , "h" : self ._fb_h }
530519
531520 r = self ._get (self .req_url .LOGOUT , data )
532521
@@ -551,8 +540,8 @@ def _getThread(self, given_thread_id=None, given_thread_type=None):
551540 :rtype: tuple
552541 """
553542 if given_thread_id is None :
554- if self .default_thread_id is not None :
555- return self .default_thread_id , self .default_thread_type
543+ if self ._default_thread_id is not None :
544+ return self ._default_thread_id , self ._default_thread_type
556545 else :
557546 raise ValueError ("Thread ID is not set" )
558547 else :
@@ -566,8 +555,8 @@ def setDefaultThread(self, thread_id, thread_type):
566555 :param thread_type: See :ref:`intro_threads`
567556 :type thread_type: models.ThreadType
568557 """
569- self .default_thread_id = thread_id
570- self .default_thread_type = thread_type
558+ self ._default_thread_id = thread_id
559+ self ._default_thread_type = thread_type
571560
572561 def resetDefaultThread (self ):
573562 """Resets default thread"""
@@ -672,7 +661,7 @@ def fetchAllUsers(self):
672661 :rtype: list
673662 :raises: FBchatException if request failed
674663 """
675- data = {"viewer" : self .uid }
664+ data = {"viewer" : self ._uid }
676665 j = self ._post (
677666 self .req_url .ALL_USERS , query = data , fix_request = True , as_json = True
678667 )
@@ -1260,13 +1249,13 @@ def _getSendData(self, message=None, thread_id=None, thread_type=ThreadType.USER
12601249 messageAndOTID = generateOfflineThreadingID ()
12611250 timestamp = now ()
12621251 data = {
1263- "client" : self . client ,
1264- "author" : "fbid:{}" .format (self .uid ),
1252+ "client" : "mercury" ,
1253+ "author" : "fbid:{}" .format (self ._uid ),
12651254 "timestamp" : timestamp ,
12661255 "source" : "source:chat:web" ,
12671256 "offline_threading_id" : messageAndOTID ,
12681257 "message_id" : messageAndOTID ,
1269- "threading_id" : generateMessageID (self .client_id ),
1258+ "threading_id" : generateMessageID (self ._client_id ),
12701259 "ephemeral_ttl_mode:" : "0" ,
12711260 }
12721261
@@ -1331,7 +1320,7 @@ def _doSendRequest(self, data, get_thread_id=False):
13311320 # update JS token if received in response
13321321 fb_dtsg = get_jsmods_require (j , 2 )
13331322 if fb_dtsg is not None :
1334- self .payloadDefault ["fb_dtsg" ] = fb_dtsg
1323+ self ._payload_default ["fb_dtsg" ] = fb_dtsg
13351324
13361325 try :
13371326 message_ids = [
@@ -1738,7 +1727,7 @@ def createGroup(self, message, user_ids):
17381727 if len (user_ids ) < 2 :
17391728 raise FBchatUserError ("Error when creating group: Not enough participants" )
17401729
1741- for i , user_id in enumerate (user_ids + [self .uid ]):
1730+ for i , user_id in enumerate (user_ids + [self ._uid ]):
17421731 data ["specific_to_list[{}]" .format (i )] = "fbid:{}" .format (user_id )
17431732
17441733 message_id , thread_id = self ._doSendRequest (data , get_thread_id = True )
@@ -1766,7 +1755,7 @@ def addUsersToGroup(self, user_ids, thread_id=None):
17661755 user_ids = require_list (user_ids )
17671756
17681757 for i , user_id in enumerate (user_ids ):
1769- if user_id == self .uid :
1758+ if user_id == self ._uid :
17701759 raise FBchatUserError (
17711760 "Error when adding users: Cannot add self to group thread"
17721761 )
@@ -1842,7 +1831,7 @@ def _usersApproval(self, user_ids, approve, thread_id=None):
18421831
18431832 data = {
18441833 "client_mutation_id" : "0" ,
1845- "actor_id" : self .uid ,
1834+ "actor_id" : self ._uid ,
18461835 "thread_fbid" : thread_id ,
18471836 "user_ids" : user_ids ,
18481837 "response" : "ACCEPT" if approve else "DENY" ,
@@ -2001,7 +1990,7 @@ def reactToMessage(self, message_id, reaction):
20011990 data = {
20021991 "action" : "ADD_REACTION" if reaction else "REMOVE_REACTION" ,
20031992 "client_mutation_id" : "1" ,
2004- "actor_id" : self .uid ,
1993+ "actor_id" : self ._uid ,
20051994 "message_id" : str (message_id ),
20061995 "reaction" : reaction .value if reaction else None ,
20071996 }
@@ -2097,7 +2086,7 @@ def createPoll(self, poll, thread_id=None):
20972086
20982087 # We're using ordered dicts, because the Facebook endpoint that parses the POST
20992088 # parameters is badly implemented, and deals with ordering the options wrongly.
2100- # This also means we had to change `client.payloadDefault ` to an ordered dict,
2089+ # This also means we had to change `client._payload_default ` to an ordered dict,
21012090 # since that's being copied in between this point and the `requests` call
21022091 #
21032092 # If you can find a way to fix this for the endpoint, or if you find another
@@ -2405,14 +2394,14 @@ def unmuteThreadMentions(self, thread_id=None):
24052394
24062395 def _ping (self ):
24072396 data = {
2408- "channel" : self .user_channel ,
2409- "clientid" : self .client_id ,
2397+ "channel" : "p_" + self ._uid ,
2398+ "clientid" : self ._client_id ,
24102399 "partition" : - 2 ,
24112400 "cap" : 0 ,
2412- "uid" : self .uid ,
2413- "sticky_token" : self .sticky ,
2414- "sticky_pool" : self .pool ,
2415- "viewer_uid" : self .uid ,
2401+ "uid" : self ._uid ,
2402+ "sticky_token" : self ._sticky ,
2403+ "sticky_pool" : self ._pool ,
2404+ "viewer_uid" : self ._uid ,
24162405 "state" : "active" ,
24172406 }
24182407 self ._get (self .req_url .PING , data , fix_request = True , as_json = False )
@@ -2421,9 +2410,9 @@ def _pullMessage(self):
24212410 """Call pull api with seq value to get message data."""
24222411 data = {
24232412 "msgs_recv" : 0 ,
2424- "sticky_token" : self .sticky ,
2425- "sticky_pool" : self .pool ,
2426- "clientid" : self .client_id ,
2413+ "sticky_token" : self ._sticky ,
2414+ "sticky_pool" : self ._pool ,
2415+ "clientid" : self ._client_id ,
24272416 "state" : "active" if self ._markAlive else "offline" ,
24282417 }
24292418 return self ._get (self .req_url .STICKY , data , fix_request = True , as_json = True )
@@ -2976,11 +2965,11 @@ def getThreadIdAndThreadType(msg_metadata):
29762965
29772966 def _parseMessage (self , content ):
29782967 """Get message and author name from content. May contain multiple messages in the content."""
2979- self .seq = content .get ("seq" , "0" )
2968+ self ._seq = content .get ("seq" , "0" )
29802969
29812970 if "lb_info" in content :
2982- self .sticky = content ["lb_info" ]["sticky" ]
2983- self .pool = content ["lb_info" ]["pool" ]
2971+ self ._sticky = content ["lb_info" ]["sticky" ]
2972+ self ._pool = content ["lb_info" ]["pool" ]
29842973
29852974 if "batches" in content :
29862975 for batch in content ["batches" ]:
@@ -3013,7 +3002,7 @@ def _parseMessage(self, content):
30133002 thread_id = str (thread_id )
30143003 else :
30153004 thread_type = ThreadType .USER
3016- if author_id == self .uid :
3005+ if author_id == self ._uid :
30173006 thread_id = m .get ("to" )
30183007 else :
30193008 thread_id = author_id
@@ -3126,7 +3115,7 @@ def doOneListen(self, markAlive=None):
31263115 def stopListening (self ):
31273116 """Cleans up the variables from startListening"""
31283117 self .listening = False
3129- self .sticky , self .pool = (None , None )
3118+ self ._sticky , self ._pool = (None , None )
31303119
31313120 def listen (self , markAlive = None ):
31323121 """
0 commit comments