Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 2 methods: fetch all threads, fetch all users involved in threads #300

Merged
merged 13 commits into from
Jan 31, 2019
76 changes: 76 additions & 0 deletions fbchat/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,82 @@ def _forcedFetch(self, thread_id, mid):
}))
return j

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
"""
threads = []

last_thread_timestamp = None
while True:
# break if limit is exceeded
if limit and len(threads) >= limit:
break

# 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):
break

# Return only threads between before and after (if set)
if before is not None or after is not None:
for t in threads:
last_message_timestamp = int(t.last_message_timestamp)
if (before is not None and last_message_timestamp > before) or \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems kinda unclean that this is duplicated further up, is there a way we can refactor, and merge these?

(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.

:param threads: models.Thread: List of threads to check for users
:return: :class:`models.User` objects
:rtype: list
: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 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):
"""
Gets all users the client is currently chatting with
Expand Down
5 changes: 5 additions & 0 deletions tests/test_fetch.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down