From 5019671b35425a087facb1352111f26798cc3177 Mon Sep 17 00:00:00 2001 From: Taehoon Kim Date: Thu, 4 May 2017 09:30:27 -0700 Subject: [PATCH 01/12] version up from 0.8.2 to 0.8.5 thx to @Dainius14 --- fbchat/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fbchat/__init__.py b/fbchat/__init__.py index 6e81747..0007d56 100644 --- a/fbchat/__init__.py +++ b/fbchat/__init__.py @@ -15,7 +15,7 @@ from .client import * __copyright__ = 'Copyright 2015 by Taehoon Kim' -__version__ = '0.8.2' +__version__ = '0.8.5' __license__ = 'BSD' __author__ = 'Taehoon Kim; Moreels Pieter-Jan' __email__ = 'carpedm20@gmail.com' From dc1d158059d55884e7243581347ff118e5770db2 Mon Sep 17 00:00:00 2001 From: Tom McAdam Date: Fri, 5 May 2017 17:03:21 +0900 Subject: [PATCH 02/12] Fix for getUserInfo method throwing an error The participants property on thread object is return a list of unicodes. If you then pass these unicodes to getUserInfo, they are not being picked up in the if statement on line 905 in the fbidStrip method, so the fbidStrip method was returning a tuple of None objects. The conditional now matches and reformats both str and unicode objects. --- fbchat/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fbchat/client.py b/fbchat/client.py index 0595db3..fd74a20 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -902,7 +902,7 @@ class Client(object): if type(_fbid) == int: return _fbid - if type(_fbid) == str and 'fbid:' in _fbid: + if type(_fbid) in [str, unicode] and 'fbid:' in _fbid: return int(_fbid[5:]) user_ids = [fbidStrip(uid) for uid in user_ids] From 05ca457946913bdd5bd0062bf8c8f468af6f2c78 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 5 May 2017 11:39:51 +0200 Subject: [PATCH 03/12] Added a helper function `strip_to_json` --- fbchat/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/fbchat/utils.py b/fbchat/utils.py index d8d3e65..fdf2580 100644 --- a/fbchat/utils.py +++ b/fbchat/utils.py @@ -14,8 +14,11 @@ USER_AGENTS = [ def now(): return int(time()*1000) +def strip_to_json(text): + return re.sub(r"^[^{]*", '', text, 1); + def get_json(text): - return json.loads(re.sub(r"^[^{]*", '', text, 1)) + return json.loads(strip_to_json(text)) def digit_to_char(digit): if digit < 10: From a280555536cf475bd4bda4cee7d74f8add42ada7 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 5 May 2017 14:24:03 +0200 Subject: [PATCH 04/12] Cleaned up login process, changed `saveSession` and `loadSession` and added `is_logged_in` --- fbchat/client.py | 101 +++++++++++++++++++++-------------------------- fbchat/utils.py | 14 +++++++ 2 files changed, 58 insertions(+), 57 deletions(-) diff --git a/fbchat/client.py b/fbchat/client.py index 0595db3..6129278 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -66,42 +66,27 @@ class Client(object): documentation for the API. """ - def __init__(self, email, password, debug=True, info_log=True, user_agent=None, max_retries=5, do_login=True): + def __init__(self, email, password, debug=True, info_log=True, user_agent=None, max_retries=5, session_cookies=None): """A client for the Facebook Chat (Messenger). :param email: Facebook `email` or `id` or `phone number` :param password: Facebook account password - - import fbchat - chat = fbchat.Client(email, password) + :param debug: Configures the logger to `debug` logging_level + :param info_log: Configures the logger to `info` logging_level + :param user_agent: Custom user agent to use when sending requests. If `None`, user agent will be chosen from a premade list (see utils.py) + :param max_retries: Maximum number of times to retry login + :param session_cookies: Cookie dict from a previous session (Will default to login if these are invalid) """ - if do_login and not (email and password): - raise Exception("Email and password not found.") - self.is_def_recipient_set = False - self.debug = debug self.sticky, self.pool = (None, None) self._session = requests.session() self.req_counter = 1 self.seq = "0" - self.payloadDefault={} + self.payloadDefault = {} self.client = 'mercury' self.listening = False - self.GENDERS = { - 0: 'unknown', - 1: 'female_singular', - 2: 'male_singular', - 3: 'female_singular_guess', - 4: 'male_singular_guess', - 5: 'mixed', - 6: 'neuter_singular', - 7: 'unknown_singular', - 8: 'female_plural', - 9: 'male_plural', - 10: 'neuter_plural', - 11: 'unknown_plural', - } + self.threads = [] if not user_agent: user_agent = choice(USER_AGENTS) @@ -114,7 +99,7 @@ class Client(object): 'Connection' : 'keep-alive', } - # Configure the logger differently based on the 'debug' parameter + # Configure the logger differently based on the 'debug' and 'info_log' parameters if debug: logging_level = logging.DEBUG elif info_log: @@ -128,10 +113,12 @@ class Client(object): log.addHandler(handler) log.setLevel(logging.DEBUG) - if do_login: + # 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) or not self.is_logged_in(): self.login(email, password, max_retries) - self.threads = [] + def _init_logging(self): + def _console(self, msg): """Assumes an INFO level and log it. @@ -169,11 +156,11 @@ class Client(object): return payload def _get(self, url, query=None, timeout=30): - payload=self._generatePayload(query) + payload = self._generatePayload(query) return self._session.get(url, headers=self._header, params=payload, timeout=timeout) def _post(self, url, query=None, timeout=30): - payload=self._generatePayload(query) + payload = self._generatePayload(query) return self._session.post(url, headers=self._header, data=payload, timeout=timeout) def _cleanGet(self, url, query=None, timeout=30): @@ -302,40 +289,40 @@ class Client(object): r = self._cleanPost(CheckpointURL, data) return r - def saveSession(self, sessionfile): - """Dumps the session cookies to (sessionfile). - WILL OVERWRITE ANY EXISTING FILE + def is_logged_in(self): + # Send a request to the login url, to see if we're directed to the home page. + r = self._cleanGet(LoginURL) + if 'home' in r.url: + return True + else: + return False - :param sessionfile: location of saved session file + def getSession(self): + """Returns the session cookies""" + return self._session.cookies.get_dict() + + def setSession(self, session_cookies): + """Loads session cookies + + :param session_cookies: dictionary containing session cookies + Return false if session_cookies does not contain proper cookies """ - log.info('Saving session') - with open(sessionfile, 'w') as f: - # Grab cookies from current session, and save them as JSON - f.write(json.dumps(self._session.cookies.get_dict(), ensure_ascii=False)) - - def loadSession(self, sessionfile): - """Loads session cookies from (sessionfile) - - :param sessionfile: location of saved session file - """ - - log.info('Loading session') - with open(sessionfile, 'r') as f: - try: - j = json.load(f) - if not j or 'c_user' not in j: - return False - # Load cookies into current session - self._session.cookies = requests.cookies.merge_cookies(self._session.cookies, j) - self._post_login() - return True - except Exception as e: - raise Exception('Invalid json in {}, or bad merging of cookies'.format(sessionfile)) + # Quick check to see if session_cookies is formatted properly + if not session_cookies or 'c_user' not in session_cookies: + return False + + # Load cookies into current session + self._session.cookies = requests.cookies.merge_cookies(self._session.cookies, session_cookies) + self._post_login() + return True def login(self, email, password, max_retries=5): # Logging in - log.info("Logging in...") + log.info("Logging in {}...".format(email)) + + if not (email and password): + raise Exception("Email and password not set.") self.email = email self.password = password @@ -346,7 +333,7 @@ class Client(object): time.sleep(1) continue else: - log.info("Login successful.") + log.info("Login of {} successful.".format(email)) break else: raise Exception("Login failed. Check email/password.") diff --git a/fbchat/utils.py b/fbchat/utils.py index fdf2580..e9f4076 100644 --- a/fbchat/utils.py +++ b/fbchat/utils.py @@ -10,6 +10,20 @@ USER_AGENTS = [ "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6" ] +GENDERS = { + 0: 'unknown', + 1: 'female_singular', + 2: 'male_singular', + 3: 'female_singular_guess', + 4: 'male_singular_guess', + 5: 'mixed', + 6: 'neuter_singular', + 7: 'unknown_singular', + 8: 'female_plural', + 9: 'male_plural', + 10: 'neuter_plural', + 11: 'unknown_plural', +} def now(): return int(time()*1000) From fa026021b256fd97a90980b8f4a27a46d64a87b2 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 5 May 2017 18:54:35 +0200 Subject: [PATCH 05/12] Added unit/integration tests --- .gitignore | 5 +- fbchat/client.py | 8 +-- fbchat/utils.py | 2 +- test_image.png | Bin 0 -> 1622 bytes tests.py | 167 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 174 insertions(+), 8 deletions(-) create mode 100644 test_image.png create mode 100644 tests.py diff --git a/.gitignore b/.gitignore index 5b3da8d..dbca6db 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ *py[co] -#test +# Test scripts *.sh # Packages @@ -21,3 +21,6 @@ develop-eggs # Sphinx documentation docs/_build/ + +# Data for tests +tests.data \ No newline at end of file diff --git a/fbchat/client.py b/fbchat/client.py index 6129278..c8aca48 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -57,6 +57,7 @@ facebookEncoding = 'UTF-8' # Log settings log = logging.getLogger("client") +log.setLevel(logging.DEBUG) class Client(object): @@ -111,15 +112,11 @@ class Client(object): handler = logging.StreamHandler() handler.setLevel(logging_level) log.addHandler(handler) - log.setLevel(logging.DEBUG) # 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) or not self.is_logged_in(): self.login(email, password, max_retries) - def _init_logging(self): - - def _console(self, msg): """Assumes an INFO level and log it. @@ -329,7 +326,7 @@ class Client(object): for i in range(1, max_retries+1): if not self._login(): - log.warning("Attempt #{} failed{}".format(i,{True:', retrying'}.get(i<5,''))) + log.warning("Attempt #{} failed{}".format(i,{True:', retrying'}.get(i < max_retries, ''))) time.sleep(1) continue else: @@ -421,7 +418,6 @@ class Client(object): return users - def getUsers(self, name): """Find and get user by his/her name diff --git a/fbchat/utils.py b/fbchat/utils.py index e9f4076..292d422 100644 --- a/fbchat/utils.py +++ b/fbchat/utils.py @@ -29,7 +29,7 @@ def now(): return int(time()*1000) def strip_to_json(text): - return re.sub(r"^[^{]*", '', text, 1); + return text[text.index('{'):] def get_json(text): return json.loads(strip_to_json(text)) diff --git a/test_image.png b/test_image.png new file mode 100644 index 0000000000000000000000000000000000000000..1ca7988d27af9fecf0230ba7e3b02ad6f969c722 GIT binary patch literal 1622 zcmeAS@N?(olHy`uVBq!ia0y~yU}ykg4mJh`hQoG=rx_R+Sc;uILpV4%IBGajIv5xj zI14-?iy0WWg+Z8+Vb&Z81_st2o-U3d6?5Ls_0EV56=~bQizt3BKpR@PfK7|$mCk{mvqT@y+^B#^G z{vPJwAWaiHTlMqI&&}uVb$G|>=KUdQ=`|_FcbP`*D{r#+OnY$D)6AEt{H*ol%`XgO ziscFNvN}fq(JI^-8uoc4l`TW!T@vk_tt;y7U%2%VA+_s9V7QV(!2H$p;)0 z4fcy?~9!0B&0Cdzdl)Kn6XxIJ&!?mUP7SbPWF@M7?_XEF=BXU zwL{k8(9G=Tbq^Sgek7zgZ4T|e zK4X(qf@NO1PJHd|jQ=weWbHRpc;vd8XMr!SpeQ_YedoXs55di$kOKC{omYkXBq zS$oBPq`B>R-|(90QRK?CjMpB_o3=Iilk_i{h)IjCtv&Fv^3hzS|FzSyGaHs3xZbv> z@&$83=)Una}Lzt#A_<+hr) z7qoS=?r{8>U~tdy!KEvUFU^UPd{B1k>sFK91*>z7cM6-&%zPxPcz061wwUxMw#$6X zF^=18OCw+A#~gkhy`%iw!jF6t-{vOi@)YQOSgq(E`$ac#`yAtydlS`X5l&q!=FeHh_%lxKs$_6I><9Hxg(-r;Yox-f{X z;od1*VS91to`K@lu+_`1UXprXdicF(nJmM( zdFgYkdG|~ETw;k9+GlZcnf}b(O%YbiUmovD?3P=z>y_}G!}Hme8Xaie(H|{p>rr}L zAl>Z88xI5fl^a!F9L&v*`tsn&BJ*X(_lmH+I5Kxum)w*aUt}aD6uM=%lrfm63b?C0 z*1Y4qO+n|b#>_cxE_yx>=O3PCe?9NvRke!W){+WEDGevU1w@AocFysnhRHj_6+b 0) + + def test_getUsers(self): + users = client.getUsers("Mark Zuckerberg") + self.assertTrue(len(users) > 0) + + u = users[0] + + # Test if values are set correctly + self.assertTrue(isinstance(u.uid, int)) + self.assertEquals(u.type, 'user') + self.assertEquals(u.photo[:4], 'http') + self.assertEquals(u.url[:4], 'http') + self.assertEquals(u.name, 'Mark Zuckerberg') + self.assertTrue(u.score > 0) + + def test_send_likes(self): + self.assertTrue(client.send(client.uid, like='s')) + self.assertTrue(client.send(client.uid, like='m')) + self.assertTrue(client.send(client.uid, like='l')) + self.assertTrue(client.send(group_uid, like='s', is_user=False)) + self.assertTrue(client.send(group_uid, like='m', is_user=False)) + self.assertTrue(client.send(group_uid, like='l', is_user=False)) + + def test_send(self): + self.assertTrue(client.send(client.uid, message='test_send_user')) + self.assertTrue(client.send(group_uid, message='test_send_group', is_user=False)) + + def test_send_images(self): + image_url = 'https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-image-128.png' + image_local_url = path.join(path.dirname(__file__), 'test_image.png') + self.assertTrue(client.sendRemoteImage(client.uid, message='test_send_user_images_remote', image=image_url)) + self.assertTrue(client.sendLocalImage(client.uid, message='test_send_user_images_local', image=image_local_url)) + self.assertTrue(client.sendRemoteImage(group_uid, message='test_send_group_images_remote', is_user=False, image=image_url)) + self.assertTrue(client.sendLocalImage(group_uid, message='test_send_group_images_local', is_user=False, image=image_local_url)) + + def test_getThreadInfo(self): + info = client.getThreadInfo(client.uid, last_n=1) + self.assertEquals(info[0].author, 'fbid:' + str(client.uid)) + client.send(group_uid, message='test_getThreadInfo', is_user=False) + info = client.getThreadInfo(group_uid, last_n=1, is_user=False) + self.assertEquals(info[0].author, 'fbid:' + str(client.uid)) + self.assertEquals(info[0].body, 'test_getThreadInfo') + + def test_markAs(self): + # To be implemented (requires some form of manual watching) + pass + + def test_listen(self): + client.do_one_listen() + + def test_getUserInfo(self): + info = client.getUserInfo(4) + self.assertEquals(info['name'], 'Mark Zuckerberg') + + def test_remove_add_from_chat(self): + client.remove_user_from_chat(group_uid, user_uid) + client.add_users_to_chat(group_uid, user_uid) + + +def start_test(param_client, param_group_uid, param_user_uid, tests=[]): + global client + global group_uid + global user_uid + + client = param_client + group_uid = param_group_uid + user_uid = param_user_uid + + if len(tests) == 0: + suite = unittest.TestLoader().loadTestsFromTestCase(TestFbchat) + else: + suite = unittest.TestSuite(map(TestFbchat, tests)) + print ('Starting test(s)') + unittest.TextTestRunner(verbosity=2).run(suite) + + + +if __name__ == '__main__': + # Python 3 does not use raw_input, whereas Python 2 does + try: + input = raw_input + except Exception as e: + pass + + try: + with open(path.join(path.dirname(__file__), 'tests.data'), 'r') as f: + content = f.readlines() + content = [x.strip() for x in content] + email = content[0] + password = content[1] + group_uid = content[2] + user_uid = content[3] + except (IOError, IndexError) as e: + email = input('Email: ') + password = getpass.getpass() + group_uid = input('Please enter a group uid (To test group functionality): ') + user_uid = input('Please enter a user uid (To test kicking/adding functionality): ') + + print ('Logging in') + client = CustomClient(email, password) + + # Warning! Taking user input directly like this could be dangerous! Use only for testing purposes! + start_test(client, group_uid, user_uid, sys.argv[1:]) + From 2280d31cbbf07e50b182a369be85b8558c9f7da0 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 5 May 2017 18:59:38 +0200 Subject: [PATCH 06/12] Added an extra test --- tests.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tests.py b/tests.py index d4ef374..f2d1e0b 100644 --- a/tests.py +++ b/tests.py @@ -28,10 +28,6 @@ If you've made any changes to the 2FA functionality, test it with a 2FA enabled """ -class CustomClient(fbchat.Client): - def on_message_new(self, *args, **kwargs): - self.listening = False - class TestFbchat(unittest.TestCase): def test_login_functions(self): self.assertTrue(client.is_logged_in()) @@ -50,7 +46,7 @@ class TestFbchat(unittest.TestCase): def test_sessions(self): global client session_cookies = client.getSession() - client = CustomClient(email, password, session_cookies=session_cookies) + client = fbchat.Client(email, password, session_cookies=session_cookies) self.assertTrue(client.is_logged_in()) @@ -116,8 +112,11 @@ class TestFbchat(unittest.TestCase): self.assertEquals(info['name'], 'Mark Zuckerberg') def test_remove_add_from_chat(self): - client.remove_user_from_chat(group_uid, user_uid) - client.add_users_to_chat(group_uid, user_uid) + self.assertTrue(client.remove_user_from_chat(group_uid, user_uid)) + self.assertTrue(client.add_users_to_chat(group_uid, user_uid)) + + def test_changeThreadTitle(self): + self.assertTrue(client.changeThreadTitle(group_uid, 'test_changeThreadTitle')) def start_test(param_client, param_group_uid, param_user_uid, tests=[]): @@ -160,7 +159,7 @@ if __name__ == '__main__': user_uid = input('Please enter a user uid (To test kicking/adding functionality): ') print ('Logging in') - client = CustomClient(email, password) + client = fbchat.Client(email, password) # Warning! Taking user input directly like this could be dangerous! Use only for testing purposes! start_test(client, group_uid, user_uid, sys.argv[1:]) From 64243c20b2a7e4424371e404229e9ad7bf9a4378 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 5 May 2017 20:12:17 +0200 Subject: [PATCH 07/12] Fixed tests, and improved send() --- fbchat/client.py | 33 ++++++++++++++++++++++++--------- tests.py | 14 ++++++++------ 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/fbchat/client.py b/fbchat/client.py index c8aca48..ffa77cc 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -451,6 +451,8 @@ class Client(object): :param like: size of the like sticker you want to send :param image_id: id for the image to send, gotten from the UploadURL :param add_user_ids: a list of user ids to add to a chat + + returns a list of message ids of the sent message(s) """ if self.is_def_recipient_set: @@ -521,11 +523,10 @@ class Client(object): data["sticker_id"] = sticker r = self._post(SendURL, data) - - if r.ok: - log.info('Message sent.') - else: - log.info('Message not sent.') + + if not r.ok: + log.warning('Error when sending message: Got {} response'.format(r.status_code)) + return False if isinstance(r._content, str) is False: r._content = r._content.decode(facebookEncoding) @@ -534,10 +535,18 @@ class Client(object): # 'errorDescription' is in the users own language! log.warning('Error #{} when sending message: {}'.format(j['error'], j['errorDescription'])) return False + + message_ids = [] + try: + message_ids += [action['message_id'] for action in j['payload']['actions'] if 'message_id' in action] + except KeyError as e: + log.warning('Error when sending message: No message ids could be found') + return False + log.info('Message sent.') log.debug("Sending {}".format(r)) log.debug("With data {}".format(data)) - return True + return message_ids def sendRemoteImage(self, recipient_id=None, message=None, is_user=True, image=''): @@ -826,10 +835,10 @@ class Client(object): fbid = m['delta']['messageMetadata']['actorFbId'] self.on_message_new(mid, fbid, message, m, recipient_id, thread_type) elif m['type'] in ['jewel_requests_add']: - from_id = m['from'] - self.on_friend_request(from_id) + from_id = m['from'] + self.on_friend_request(from_id) else: - log.debug("Unknown type {}".format(m)) + self.on_unknown_type(m) except Exception as e: # ex_type, ex, tb = sys.exc_info() self.on_message_error(sys.exc_info(), m) @@ -1021,3 +1030,9 @@ class Client(object): def on_qprimer(self, timestamp): pass + + + def on_unknown_type(self, m): + """subclass Client and override this method to add custom behavior on event""" + log.debug("Unknown type {}".format(m)) + diff --git a/tests.py b/tests.py index f2d1e0b..533e0d2 100644 --- a/tests.py +++ b/tests.py @@ -14,17 +14,19 @@ fbchat.log.setLevel(100) """ Tests for fbchat +~~~~~~~~~~~~~~~~ To use these tests, put: - email - password - a group_uid -- a user_uid whom will be kicked from that group and then added again +- a user_uid (the user will be kicked from the group and then added again) (seperated these by a newline) in a file called `tests.data`, or type them manually in the terminal prompts Please remember to test both python v. 2.7 and python v. 3.6! If you've made any changes to the 2FA functionality, test it with a 2FA enabled account +If you only want to execute specific tests, pass the function names in the commandline """ @@ -56,21 +58,21 @@ class TestFbchat(unittest.TestCase): def test_getAllUsers(self): users = client.getAllUsers() - self.assertTrue(len(users) > 0) + self.assertGreater(len(users), 0) def test_getUsers(self): users = client.getUsers("Mark Zuckerberg") - self.assertTrue(len(users) > 0) + self.assertGreater(len(users), 0) u = users[0] # Test if values are set correctly - self.assertTrue(isinstance(u.uid, int)) + self.assertIsInstance(u.uid, int) self.assertEquals(u.type, 'user') self.assertEquals(u.photo[:4], 'http') self.assertEquals(u.url[:4], 'http') self.assertEquals(u.name, 'Mark Zuckerberg') - self.assertTrue(u.score > 0) + self.assertGreater(u.score, 0) def test_send_likes(self): self.assertTrue(client.send(client.uid, like='s')) @@ -147,7 +149,7 @@ if __name__ == '__main__': try: with open(path.join(path.dirname(__file__), 'tests.data'), 'r') as f: content = f.readlines() - content = [x.strip() for x in content] + content = [x.strip() for x in content if len(x.strip()) != 0] email = content[0] password = content[1] group_uid = content[2] From f4165462dc9d3a194ac191ef4d244a1aa4f572c7 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 5 May 2017 20:16:40 +0200 Subject: [PATCH 08/12] Updated README, to reflect changes --- README.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 816e6a0..c02a748 100644 --- a/README.rst +++ b/README.rst @@ -99,7 +99,8 @@ Saving session .. code-block:: python - client.saveSession(sessionfile) + session_cookies = client.setSession() + # save session_cookies Loading session @@ -107,8 +108,9 @@ Loading session .. code-block:: python - client = fbchat.Client(None, None, do_login=False) - client.loadSession(sessionfile) + client = fbchat.Client(None, None, session_cookies=session_cookies) + # OR + client.setSession(session_cookies) Authors From 3b4a369586733e2e15156e2dfd44af7da8ede1e2 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Fri, 5 May 2017 20:26:23 +0200 Subject: [PATCH 09/12] Improved error handling in send() --- fbchat/client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fbchat/client.py b/fbchat/client.py index ffa77cc..06d994f 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -539,7 +539,8 @@ class Client(object): message_ids = [] try: message_ids += [action['message_id'] for action in j['payload']['actions'] if 'message_id' in action] - except KeyError as e: + message_ids[0] # Try accessing element + except (KeyError, IndexError) as e: log.warning('Error when sending message: No message ids could be found') return False From c4aa6b4523c2b2de7badc2c7dd9b9c517b7ffbe2 Mon Sep 17 00:00:00 2001 From: Mads T Marquart Date: Fri, 5 May 2017 21:15:43 +0200 Subject: [PATCH 10/12] Update client.py --- fbchat/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fbchat/client.py b/fbchat/client.py index 0595db3..8d11069 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -616,7 +616,7 @@ class Client(object): # `start` doesn't matter, always returns from the last # data['messages[{}][{}][offset]'.format(key, userID)] = start data = {'messages[{}][{}][offset]'.format(key, userID): 0, - 'messages[{}][{}][limit]'.format(key, userID): last_n, + 'messages[{}][{}][limit]'.format(key, userID): last_n - 1, 'messages[{}][{}][timestamp]'.format(key, userID): now()} r = self._post(MessagesURL, query=data) From 2aa1978274cb66dab90e927d333fc8b53686ba24 Mon Sep 17 00:00:00 2001 From: Mads T Marquart Date: Fri, 5 May 2017 21:17:35 +0200 Subject: [PATCH 11/12] Update README.rst --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 816e6a0..87b5249 100644 --- a/README.rst +++ b/README.rst @@ -61,7 +61,7 @@ Getting last messages sent .. code-block:: python - last_messages = client.getThreadInfo(friend.uid,0) + last_messages = client.getThreadInfo(friend.uid, last_n=20) last_messages.reverse() # messages come in reversed order for message in last_messages: From b804dc3f9439c08452ff8a5a8a62f7a8f2c01eb6 Mon Sep 17 00:00:00 2001 From: Taehoon Kim Date: Fri, 5 May 2017 12:59:48 -0700 Subject: [PATCH 12/12] Version up thanks to @madsmtm and @tmcadam --- fbchat/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fbchat/__init__.py b/fbchat/__init__.py index 0007d56..586d267 100644 --- a/fbchat/__init__.py +++ b/fbchat/__init__.py @@ -15,7 +15,7 @@ from .client import * __copyright__ = 'Copyright 2015 by Taehoon Kim' -__version__ = '0.8.5' +__version__ = '0.9.0' __license__ = 'BSD' __author__ = 'Taehoon Kim; Moreels Pieter-Jan' __email__ = 'carpedm20@gmail.com'