From 487a2eb3e3d09f1973fe2872d3a7f0220866f136 Mon Sep 17 00:00:00 2001 From: 2FWAH <36737818+2FWAH@users.noreply.github.com> Date: Fri, 1 Jun 2018 22:59:56 +0200 Subject: [PATCH 01/12] Add fetchThreads method Add a method to get all threads in Location (INBOX, ARCHIVED...) --- fbchat/client.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/fbchat/client.py b/fbchat/client.py index 2b20d69..7930b2d 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -472,6 +472,28 @@ class Client(object): """ FETCH METHODS """ + + def fetchThreads(self, thread_location): + """ + Get all threads in thread_location. + + :param thread_location: models.ThreadLocation: INBOX, PENDING, ARCHIVED or OTHER + :return: :class:`models.Thread` objects + :rtype: list + :raises: FBchatException if request failed + """ + Threads = [] + Threads += self.fetchThreadList(thread_location) + if len(Threads) == 0: + return [] + while True: + lastThreadTimestamp = Threads[-1].last_message_timestamp + candidates = client.fetchThreadList(before=lastThreadTimestamp, thread_location=thread_location) # return at max 20 threads before lastThreadTimestamp (included) + if len(candidates) > 1: + Threads += candidates[1:] + else: + break + return Threads # from newest to oldest def fetchAllUsers(self): """ From 19457efe9bb95e969a53475424e7ff940a98cb09 Mon Sep 17 00:00:00 2001 From: 2FWAH <36737818+2FWAH@users.noreply.github.com> Date: Fri, 1 Jun 2018 23:06:02 +0200 Subject: [PATCH 02/12] Fix call to fetchThreadList Use "self" instead of "client" --- fbchat/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fbchat/client.py b/fbchat/client.py index 7930b2d..581413b 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -488,7 +488,7 @@ class Client(object): return [] while True: lastThreadTimestamp = Threads[-1].last_message_timestamp - candidates = client.fetchThreadList(before=lastThreadTimestamp, thread_location=thread_location) # return at max 20 threads before lastThreadTimestamp (included) + candidates = self.fetchThreadList(before=lastThreadTimestamp, thread_location=thread_location) # return at max 20 threads before lastThreadTimestamp (included) if len(candidates) > 1: Threads += candidates[1:] else: From abe3357e679df8bf6dcf133417aa98de46511b3b Mon Sep 17 00:00:00 2001 From: 2FWAH <36737818+2FWAH@users.noreply.github.com> Date: Fri, 1 Jun 2018 23:08:03 +0200 Subject: [PATCH 03/12] Explicit parameter thread_location --- fbchat/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fbchat/client.py b/fbchat/client.py index 581413b..7360bcc 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -483,7 +483,7 @@ class Client(object): :raises: FBchatException if request failed """ Threads = [] - Threads += self.fetchThreadList(thread_location) + Threads += self.fetchThreadList(thread_location=thread_location) if len(Threads) == 0: return [] while True: From 0767ef4902bd7de9cc3002e91583e6764a432870 Mon Sep 17 00:00:00 2001 From: 2FWAH <36737818+2FWAH@users.noreply.github.com> Date: Fri, 1 Jun 2018 23:27:34 +0200 Subject: [PATCH 04/12] Add fetchAllUsersFromThreads Add a method to get all users involved in threads (given as a parameter) --- fbchat/client.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/fbchat/client.py b/fbchat/client.py index 7360bcc..7fb4305 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -494,7 +494,29 @@ class Client(object): else: break return Threads # from newest to oldest - + + def fetchAllUsersFromThreads(client, threads): + """ + Get all users involved in threads. + + :param threads: models.Thread: List of threads to check for users + :return: :class:`models.User` objects + :rtype: list + :raises: FBchatException if request failed + """ + Users = [] + for thread in threads: + if thread.type == ThreadType.USER: + if thread.uid not in [user.uid for user in Users]: + Users.append(thread) + elif thread.type == ThreadType.GROUP: + for userID in thread.participants: + if userID not in [user.uid for user in Users]: + Users.append(self.fetchUserInfo(userID)[userID]) + else: + pass + return Users + def fetchAllUsers(self): """ Gets all users the client is currently chatting with From 0048e82151751f2b4c4d0060b46bef493390c4b2 Mon Sep 17 00:00:00 2001 From: 2FWAH <36737818+2FWAH@users.noreply.github.com> Date: Thu, 7 Jun 2018 21:58:00 +0200 Subject: [PATCH 05/12] Fix typo in fetchAllUsersFromThreads --- fbchat/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fbchat/client.py b/fbchat/client.py index 7fb4305..3f731c9 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -495,7 +495,7 @@ class Client(object): break return Threads # from newest to oldest - def fetchAllUsersFromThreads(client, threads): + def fetchAllUsersFromThreads(self, threads): """ Get all users involved in threads. From 71608845c0d8df38c7541f4b7173d45a088dd3a7 Mon Sep 17 00:00:00 2001 From: 2FWAH <36737818+2FWAH@users.noreply.github.com> Date: Tue, 12 Jun 2018 07:55:16 +0200 Subject: [PATCH 06/12] Use snake case convention --- fbchat/client.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/fbchat/client.py b/fbchat/client.py index 3f731c9..6a28813 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -482,18 +482,18 @@ class Client(object): :rtype: list :raises: FBchatException if request failed """ - Threads = [] - Threads += self.fetchThreadList(thread_location=thread_location) - if len(Threads) == 0: + threads = [] + threads += self.fetchThreadList(thread_location=thread_location) + if len(threads) == 0: return [] while True: - lastThreadTimestamp = Threads[-1].last_message_timestamp + lastThreadTimestamp = threads[-1].last_message_timestamp candidates = self.fetchThreadList(before=lastThreadTimestamp, thread_location=thread_location) # return at max 20 threads before lastThreadTimestamp (included) if len(candidates) > 1: - Threads += candidates[1:] + threads += candidates[1:] else: break - return Threads # from newest to oldest + return threads # from newest to oldest def fetchAllUsersFromThreads(self, threads): """ @@ -504,18 +504,18 @@ class Client(object): :rtype: list :raises: FBchatException if request failed """ - Users = [] + users = [] for thread in threads: if thread.type == ThreadType.USER: - if thread.uid not in [user.uid for user in Users]: - Users.append(thread) + if thread.uid not in [user.uid for user in users]: + users.append(thread) elif thread.type == ThreadType.GROUP: for userID in thread.participants: - if userID not in [user.uid for user in Users]: - Users.append(self.fetchUserInfo(userID)[userID]) + if userID not in [user.uid for user in users]: + users.append(self.fetchUserInfo(userID)[userID]) else: pass - return Users + return users def fetchAllUsers(self): """ From e0bb9960fbdb039ff07b88e05f95530ffddfc3ae Mon Sep 17 00:00:00 2001 From: 2FWAH <36737818+2FWAH@users.noreply.github.com> Date: Tue, 12 Jun 2018 08:15:53 +0200 Subject: [PATCH 07/12] Check if list is empty with if instead of len() --- fbchat/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fbchat/client.py b/fbchat/client.py index 6a28813..a1cc49e 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -484,7 +484,7 @@ class Client(object): """ threads = [] threads += self.fetchThreadList(thread_location=thread_location) - if len(threads) == 0: + if not threads: return [] while True: lastThreadTimestamp = threads[-1].last_message_timestamp From 2edb95dfdd0b95a4f0ef9a0812fcb36312003cac Mon Sep 17 00:00:00 2001 From: 2FWAH <36737818+2FWAH@users.noreply.github.com> Date: Tue, 12 Jun 2018 08:38:02 +0200 Subject: [PATCH 08/12] Fetch missing users in a single request --- fbchat/client.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/fbchat/client.py b/fbchat/client.py index a1cc49e..f720d94 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -473,7 +473,7 @@ class Client(object): FETCH METHODS """ - def fetchThreads(self, thread_location): + def fetchThreads(self, thread_location, after=None, limit=None): """ Get all threads in thread_location. @@ -505,16 +505,19 @@ class Client(object): :raises: FBchatException if request failed """ users = [] + users_to_fetch = [] # It's more efficient to fetch all users in one request for thread in threads: if thread.type == ThreadType.USER: if thread.uid not in [user.uid for user in users]: users.append(thread) elif thread.type == ThreadType.GROUP: - for userID in thread.participants: - if userID not in [user.uid for user in users]: - users.append(self.fetchUserInfo(userID)[userID]) + for user_id in thread.participants: + if user_id not in [user.uid for user in users] and user_id not in users_to_fetch: + users_to_fetch.append(user_id) else: pass + for user_id,user in self.fetchUserInfo(*users_to_fetch).items(): + users.append(user) return users def fetchAllUsers(self): From d4859b675a7d48607fc0d8ce41d3b655c8b3d327 Mon Sep 17 00:00:00 2001 From: 2FWAH <36737818+2FWAH@users.noreply.github.com> Date: Fri, 21 Sep 2018 17:36:16 +0200 Subject: [PATCH 09/12] Fix ident for _forcedFetch --- fbchat/client.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fbchat/client.py b/fbchat/client.py index 93c1f2d..66f02a4 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -479,13 +479,13 @@ class Client(object): """ def _forcedFetch(self, thread_id, mid): - j = self.graphql_request(GraphQL(doc_id='1768656253222505', params={ - 'thread_and_message_id': { - 'thread_id': thread_id, - 'message_id': mid - } - })) - return j + j = self.graphql_request(GraphQL(doc_id='1768656253222505', params={ + 'thread_and_message_id': { + 'thread_id': thread_id, + 'message_id': mid + } + })) + return j def fetchThreads(self, thread_location, after=None, limit=None): """ From 5fa1d8619176af0931b89f0d34536e528ee38ff8 Mon Sep 17 00:00:00 2001 From: 2FWAH <36737818+2FWAH@users.noreply.github.com> Date: Fri, 21 Sep 2018 19:12:46 +0200 Subject: [PATCH 10/12] Add before, after and limit parameters to fetchThreads --- fbchat/client.py | 41 +++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/fbchat/client.py b/fbchat/client.py index 66f02a4..95bd4e6 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -487,11 +487,15 @@ class Client(object): })) return j - def fetchThreads(self, thread_location, after=None, limit=None): + def fetchThreads(self, thread_location, before=None, after=None, limit=None): """ Get all threads in thread_location. + Threads will be sorted from newest to oldest. :param thread_location: models.ThreadLocation: INBOX, PENDING, ARCHIVED or OTHER + :param before: Fetch only thread before this epoch (in ms) (default all threads) + :param after: Fetch only thread after this epoch (in ms) (default all threads) + :param limit: The max. amount of threads to fetch (default all threads) :return: :class:`models.Thread` objects :rtype: list :raises: FBchatException if request failed @@ -500,15 +504,40 @@ class Client(object): threads += self.fetchThreadList(thread_location=thread_location) if not threads: return [] + while True: - lastThreadTimestamp = threads[-1].last_message_timestamp - candidates = self.fetchThreadList(before=lastThreadTimestamp, thread_location=thread_location) # return at max 20 threads before lastThreadTimestamp (included) + # break if limit is exceeded + if limit and len(threads) >= limit: + break + + last_thread_timestamp = threads[-1].last_message_timestamp + # fetchThreadList returns at max 20 threads before last_thread_timestamp (included) + candidates = self.fetchThreadList(before=last_thread_timestamp, + thread_location=thread_location + ) if len(candidates) > 1: threads += candidates[1:] - else: + else: # End of threads break - return threads # from newest to oldest - + + # FB returns a sorted list of threads + if (before is not None and int(last_thread_timestamp) > before) or \ + (after is not None and int(last_thread_timestamp) < after): + break + + # Return only threads between before and after (if set) + if before is not None or after is not None: + for t in list(threads): + last_message_timestamp = int(t.last_message_timestamp) + if (before is not None and last_message_timestamp > before) or \ + (after is not None and last_message_timestamp < after): + threads.remove(t) + + if limit and len(threads) > limit: + return threads[:limit] + + return threads + def fetchAllUsersFromThreads(self, threads): """ Get all users involved in threads. From af3bd55535bc569b048be7f30dd44382f4c8a211 Mon Sep 17 00:00:00 2001 From: 2FWAH <36737818+2FWAH@users.noreply.github.com> Date: Fri, 21 Sep 2018 19:31:26 +0200 Subject: [PATCH 11/12] Add basic test for fetchThreads --- tests/test_fetch.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_fetch.py b/tests/test_fetch.py index 669de22..4150664 100644 --- a/tests/test_fetch.py +++ b/tests/test_fetch.py @@ -19,6 +19,11 @@ def test_fetch_thread_list(client1): assert len(threads) == 2 +def test_fetch_threads(client1): + threads = client1.fetchThreads(limit=2) + assert len(threads) == 2 + + @pytest.mark.parametrize("emoji, emoji_size", EMOJI_LIST) def test_fetch_message_emoji(client, emoji, emoji_size): mid = client.sendEmoji(emoji, emoji_size) From 7f0da012c2fd37d7c4339a9af1f60f38163bb72f Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 31 Jan 2019 20:12:59 +0100 Subject: [PATCH 12/12] Few nitpicky fixes --- fbchat/client.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fbchat/client.py b/fbchat/client.py index 95bd4e6..939ff69 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -501,25 +501,25 @@ class Client(object): :raises: FBchatException if request failed """ threads = [] - threads += self.fetchThreadList(thread_location=thread_location) - if not threads: - return [] + last_thread_timestamp = None while True: # break if limit is exceeded if limit and len(threads) >= limit: break - last_thread_timestamp = threads[-1].last_message_timestamp # fetchThreadList returns at max 20 threads before last_thread_timestamp (included) candidates = self.fetchThreadList(before=last_thread_timestamp, thread_location=thread_location ) + if len(candidates) > 1: threads += candidates[1:] else: # End of threads break + last_thread_timestamp = threads[-1].last_message_timestamp + # FB returns a sorted list of threads if (before is not None and int(last_thread_timestamp) > before) or \ (after is not None and int(last_thread_timestamp) < after): @@ -527,7 +527,7 @@ class Client(object): # Return only threads between before and after (if set) if before is not None or after is not None: - for t in list(threads): + for t in threads: last_message_timestamp = int(t.last_message_timestamp) if (before is not None and last_message_timestamp > before) or \ (after is not None and last_message_timestamp < after): @@ -541,7 +541,7 @@ class Client(object): def fetchAllUsersFromThreads(self, threads): """ Get all users involved in threads. - + :param threads: models.Thread: List of threads to check for users :return: :class:`models.User` objects :rtype: list @@ -559,7 +559,7 @@ class Client(object): users_to_fetch.append(user_id) else: pass - for user_id,user in self.fetchUserInfo(*users_to_fetch).items(): + for user_id, user in self.fetchUserInfo(*users_to_fetch).items(): users.append(user) return users