Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
b4b8914448 | ||
|
2ea2c89b4a | ||
|
479ca59a6a | ||
|
343f987a78 | ||
|
bad9c7a4b9 | ||
|
576e0949e0 | ||
|
d807648d2b | ||
|
0ae213c240 | ||
|
08117e7a54 | ||
|
51c3226070 | ||
|
5396d19d7d | ||
|
11501e6899 | ||
|
4eb49b9119 | ||
|
4c2da22750 | ||
|
753b9cbae2 | ||
|
2c73cabe22 | ||
|
d6ca091b7b |
@@ -8,8 +8,8 @@ client = Client('<email>', '<password>')
|
||||
# Fetches a list of all users you're currently chatting with, as `User` objects
|
||||
users = client.fetchAllUsers()
|
||||
|
||||
print("users' IDs: {}".format(user.uid for user in users))
|
||||
print("users' names: {}".format(user.name for user in users))
|
||||
print("users' IDs: {}".format([user.uid for user in users]))
|
||||
print("users' names: {}".format([user.name for user in users]))
|
||||
|
||||
|
||||
# If we have a user id, we can use `fetchUserInfo` to fetch a `User` object
|
||||
@@ -18,7 +18,7 @@ user = client.fetchUserInfo('<user id>')['<user id>']
|
||||
users = client.fetchUserInfo('<1st user id>', '<2nd user id>', '<3rd user id>')
|
||||
|
||||
print("user's name: {}".format(user.name))
|
||||
print("users' names: {}".format(users[k].name for k in users))
|
||||
print("users' names: {}".format([users[k].name for k in users]))
|
||||
|
||||
|
||||
# `searchForUsers` searches for the user and gives us a list of the results,
|
||||
|
@@ -15,7 +15,7 @@ from __future__ import unicode_literals
|
||||
from .client import *
|
||||
|
||||
__title__ = 'fbchat'
|
||||
__version__ = '1.4.0'
|
||||
__version__ = '1.4.1'
|
||||
__description__ = 'Facebook Chat (Messenger) for Python'
|
||||
|
||||
__copyright__ = 'Copyright 2015 - 2018 by Taehoon Kim'
|
||||
|
@@ -544,7 +544,6 @@ class Client(object):
|
||||
|
||||
return [graphql_to_page(node) for node in j[name]['pages']['nodes']]
|
||||
|
||||
# TODO intergrate Rooms
|
||||
def searchForGroups(self, name, limit=1):
|
||||
"""
|
||||
Find and get group thread by its name
|
||||
@@ -585,7 +584,6 @@ class Client(object):
|
||||
elif node['__typename'] == 'Group':
|
||||
# We don't handle Facebook "Groups"
|
||||
pass
|
||||
# TODO Add Rooms
|
||||
else:
|
||||
log.warning('Unknown __typename: {} in {}'.format(repr(node['__typename']), node))
|
||||
|
||||
@@ -818,9 +816,6 @@ class Client(object):
|
||||
if entry.get('thread_type') == 'GROUP':
|
||||
_id = entry['thread_key']['thread_fbid']
|
||||
rtn[_id] = graphql_to_group(entry)
|
||||
elif entry.get('thread_type') == 'ROOM':
|
||||
_id = entry['thread_key']['thread_fbid']
|
||||
rtn[_id] = graphql_to_room(entry)
|
||||
elif entry.get('thread_type') == 'ONE_TO_ONE':
|
||||
_id = entry['thread_key']['other_user_id']
|
||||
if pages_and_users.get(_id) is None:
|
||||
@@ -855,14 +850,22 @@ class Client(object):
|
||||
'id': thread_id,
|
||||
'message_limit': limit,
|
||||
'load_messages': True,
|
||||
'load_read_receipts': False,
|
||||
'load_read_receipts': True,
|
||||
'before': before
|
||||
}))
|
||||
|
||||
if j.get('message_thread') is None:
|
||||
raise FBchatException('Could not fetch thread {}: {}'.format(thread_id, j))
|
||||
|
||||
return list(reversed([graphql_to_message(message) for message in j['message_thread']['messages']['nodes']]))
|
||||
messages = list(reversed([graphql_to_message(message) for message in j['message_thread']['messages']['nodes']]))
|
||||
read_receipts = j['message_thread']['read_receipts']['nodes']
|
||||
|
||||
for message in messages:
|
||||
for receipt in read_receipts:
|
||||
if int(receipt['watermark']) >= int(message.timestamp):
|
||||
message.read_by.append(receipt['actor']['id'])
|
||||
|
||||
return messages
|
||||
|
||||
def fetchThreadList(self, offset=None, limit=20, thread_location=ThreadLocation.INBOX, before=None):
|
||||
"""Get thread list of your facebook account
|
||||
@@ -1720,7 +1723,7 @@ class Client(object):
|
||||
}
|
||||
|
||||
for thread_id in thread_ids:
|
||||
data["ids[{}]".format(thread_id)] = read
|
||||
data["ids[{}]".format(thread_id)] = 'true' if read else 'false'
|
||||
|
||||
r = self._post(self.req_url.READ_STATUS, data)
|
||||
return r.ok
|
||||
@@ -1976,7 +1979,7 @@ class Client(object):
|
||||
'sticky_token': sticky,
|
||||
'sticky_pool': pool,
|
||||
'viewer_uid': self.uid,
|
||||
'state': 'active'
|
||||
'state': 'active',
|
||||
}
|
||||
self._get(self.req_url.PING, data, fix_request=True, as_json=False)
|
||||
|
||||
@@ -1996,7 +1999,7 @@ class Client(object):
|
||||
|
||||
return j['lb_info']['sticky'], j['lb_info']['pool']
|
||||
|
||||
def _pullMessage(self, sticky, pool):
|
||||
def _pullMessage(self, sticky, pool, markAlive=True):
|
||||
"""Call pull api with seq value to get message data."""
|
||||
|
||||
data = {
|
||||
@@ -2004,6 +2007,7 @@ class Client(object):
|
||||
"sticky_token": sticky,
|
||||
"sticky_pool": pool,
|
||||
"clientid": self.client_id,
|
||||
'state': 'active' if markAlive else 'offline',
|
||||
}
|
||||
|
||||
j = self._get(ReqUrl.STICKY, data, fix_request=True, as_json=True)
|
||||
@@ -2374,7 +2378,7 @@ class Client(object):
|
||||
try:
|
||||
if markAlive:
|
||||
self._ping(self.sticky, self.pool)
|
||||
content = self._pullMessage(self.sticky, self.pool)
|
||||
content = self._pullMessage(self.sticky, self.pool, markAlive)
|
||||
if content:
|
||||
self._parseMessage(content)
|
||||
except KeyboardInterrupt:
|
||||
|
@@ -42,7 +42,7 @@ def get_customization_info(thread):
|
||||
'emoji': info.get('emoji'),
|
||||
'color': graphql_color_to_enum(info.get('outgoing_bubble_color'))
|
||||
}
|
||||
if thread.get('thread_type') in ('GROUP', 'ROOM') or thread.get('is_group_thread') or thread.get('thread_key', {}).get('thread_fbid'):
|
||||
if thread.get('thread_type') == 'GROUP' or thread.get('is_group_thread') or thread.get('thread_key', {}).get('thread_fbid'):
|
||||
rtn['nicknames'] = {}
|
||||
for k in info.get('participant_customizations', []):
|
||||
rtn['nicknames'][k['participant_id']] = k.get('nickname')
|
||||
@@ -220,7 +220,9 @@ def graphql_to_user(user):
|
||||
if user.get('profile_picture') is None:
|
||||
user['profile_picture'] = {}
|
||||
c_info = get_customization_info(user)
|
||||
plan = graphql_to_plan(user['event_reminders']['nodes'][0]) if user.get('event_reminders', dict()).get('nodes') else None
|
||||
plan = None
|
||||
if user.get('event_reminders'):
|
||||
plan = graphql_to_plan(user['event_reminders']['nodes'][0]) if user['event_reminders'].get('nodes') else None
|
||||
return User(
|
||||
user['id'],
|
||||
url=user.get('url'),
|
||||
@@ -258,7 +260,9 @@ def graphql_to_thread(thread):
|
||||
else:
|
||||
last_name = user.get('name').split(first_name, 1).pop().strip()
|
||||
|
||||
plan = graphql_to_plan(thread['event_reminders']['nodes'][0]) if thread.get('event_reminders', dict()).get('nodes') else None
|
||||
plan = None
|
||||
if thread.get('event_reminders'):
|
||||
plan = graphql_to_plan(thread['event_reminders']['nodes'][0]) if thread['event_reminders'].get('nodes') else None
|
||||
|
||||
return User(
|
||||
user['id'],
|
||||
@@ -288,13 +292,19 @@ def graphql_to_group(group):
|
||||
last_message_timestamp = None
|
||||
if 'last_message' in group:
|
||||
last_message_timestamp = group['last_message']['nodes'][0]['timestamp_precise']
|
||||
plan = graphql_to_plan(group['event_reminders']['nodes'][0]) if group.get('event_reminders', dict()).get('nodes') else None
|
||||
plan = None
|
||||
if group.get('event_reminders'):
|
||||
plan = graphql_to_plan(group['event_reminders']['nodes'][0]) if group['event_reminders'].get('nodes') else None
|
||||
return Group(
|
||||
group['thread_key']['thread_fbid'],
|
||||
participants=set([node['messaging_actor']['id'] for node in group['all_participants']['nodes']]),
|
||||
nicknames=c_info.get('nicknames'),
|
||||
color=c_info.get('color'),
|
||||
emoji=c_info.get('emoji'),
|
||||
admins = set([node.get('id') for node in group.get('thread_admins')]),
|
||||
approval_mode = bool(group.get('approval_mode')) if group.get('approval_mode') is not None else None,
|
||||
approval_requests = set(node["requester"]['id'] for node in group['group_approval_queue']['nodes']) if group.get('group_approval_queue') else None,
|
||||
join_link = group['joinable_mode'].get('link'),
|
||||
photo=group['image'].get('uri'),
|
||||
name=group.get('name'),
|
||||
message_count=group.get('messages_count'),
|
||||
@@ -302,34 +312,14 @@ def graphql_to_group(group):
|
||||
plan=plan,
|
||||
)
|
||||
|
||||
def graphql_to_room(room):
|
||||
if room.get('image') is None:
|
||||
room['image'] = {}
|
||||
c_info = get_customization_info(room)
|
||||
plan = graphql_to_plan(room['event_reminders']['nodes'][0]) if room.get('event_reminders', dict()).get('nodes') else None
|
||||
return Room(
|
||||
room['thread_key']['thread_fbid'],
|
||||
participants=set([node['messaging_actor']['id'] for node in room['all_participants']['nodes']]),
|
||||
nicknames=c_info.get('nicknames'),
|
||||
color=c_info.get('color'),
|
||||
emoji=c_info.get('emoji'),
|
||||
photo=room['image'].get('uri'),
|
||||
name=room.get('name'),
|
||||
message_count=room.get('messages_count'),
|
||||
admins = set([node.get('id') for node in room.get('thread_admins')]),
|
||||
approval_mode = bool(room.get('approval_mode')),
|
||||
approval_requests = set(node.get('id') for node in room['thread_queue_metadata'].get('approval_requests', {}).get('nodes')),
|
||||
join_link = room['joinable_mode'].get('link'),
|
||||
privacy_mode = bool(room.get('privacy_mode')),
|
||||
plan=plan,
|
||||
)
|
||||
|
||||
def graphql_to_page(page):
|
||||
if page.get('profile_picture') is None:
|
||||
page['profile_picture'] = {}
|
||||
if page.get('city') is None:
|
||||
page['city'] = {}
|
||||
plan = graphql_to_plan(page['event_reminders']['nodes'][0]) if page.get('event_reminders', dict()).get('nodes') else None
|
||||
plan = None
|
||||
if page.get('event_reminders'):
|
||||
plan = graphql_to_plan(page['event_reminders']['nodes'][0]) if page['event_reminders'].get('nodes') else None
|
||||
return Page(
|
||||
page['id'],
|
||||
url=page.get('url'),
|
||||
@@ -433,6 +423,40 @@ class GraphQL(object):
|
||||
},
|
||||
outgoing_bubble_color,
|
||||
emoji
|
||||
},
|
||||
thread_admins {
|
||||
id
|
||||
},
|
||||
group_approval_queue {
|
||||
nodes {
|
||||
requester {
|
||||
id
|
||||
}
|
||||
}
|
||||
},
|
||||
approval_mode,
|
||||
joinable_mode {
|
||||
mode,
|
||||
link
|
||||
},
|
||||
event_reminders {
|
||||
nodes {
|
||||
id,
|
||||
lightweight_event_creator {
|
||||
id
|
||||
},
|
||||
time,
|
||||
location_name,
|
||||
event_title,
|
||||
event_reminder_members {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
},
|
||||
guest_list_state
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
@@ -102,8 +102,16 @@ class Group(Thread):
|
||||
color = None
|
||||
#: The groups's default emoji
|
||||
emoji = None
|
||||
# Set containing user IDs of thread admins
|
||||
admins = None
|
||||
# True if users need approval to join
|
||||
approval_mode = None
|
||||
# Set containing user IDs requesting to join
|
||||
approval_requests = None
|
||||
# Link for joining group
|
||||
join_link = None
|
||||
|
||||
def __init__(self, uid, participants=None, nicknames=None, color=None, emoji=None, **kwargs):
|
||||
def __init__(self, uid, participants=None, nicknames=None, color=None, emoji=None, admins=None, approval_mode=None, approval_requests=None, join_link=None, privacy_mode=None, **kwargs):
|
||||
"""Represents a Facebook group. Inherits `Thread`"""
|
||||
super(Group, self).__init__(ThreadType.GROUP, uid, **kwargs)
|
||||
if participants is None:
|
||||
@@ -114,24 +122,6 @@ class Group(Thread):
|
||||
self.nicknames = nicknames
|
||||
self.color = color
|
||||
self.emoji = emoji
|
||||
|
||||
|
||||
class Room(Group):
|
||||
# Set containing user IDs of thread admins
|
||||
admins = None
|
||||
# True if users need approval to join
|
||||
approval_mode = None
|
||||
# Set containing user IDs requesting to join
|
||||
approval_requests = None
|
||||
# Link for joining room
|
||||
join_link = None
|
||||
# True is room is not discoverable
|
||||
privacy_mode = None
|
||||
|
||||
def __init__(self, uid, admins=None, approval_mode=None, approval_requests=None, join_link=None, privacy_mode=None, **kwargs):
|
||||
"""Represents a Facebook room. Inherits `Group`"""
|
||||
super(Room, self).__init__(uid, **kwargs)
|
||||
self.type = ThreadType.ROOM
|
||||
if admins is None:
|
||||
admins = set()
|
||||
self.admins = admins
|
||||
@@ -140,6 +130,16 @@ class Room(Group):
|
||||
approval_requests = set()
|
||||
self.approval_requests = approval_requests
|
||||
self.join_link = join_link
|
||||
|
||||
|
||||
class Room(Group):
|
||||
# True is room is not discoverable
|
||||
privacy_mode = None
|
||||
|
||||
def __init__(self, uid, privacy_mode=None, **kwargs):
|
||||
"""Deprecated. Use :class:`Group` instead"""
|
||||
super(Room, self).__init__(uid, **kwargs)
|
||||
self.type = ThreadType.ROOM
|
||||
self.privacy_mode = privacy_mode
|
||||
|
||||
|
||||
@@ -180,6 +180,8 @@ class Message(object):
|
||||
timestamp = None
|
||||
#: Whether the message is read
|
||||
is_read = None
|
||||
#: A list of pepole IDs who read the message, works only with :func:`fbchat.Client.fetchThreadMessages`
|
||||
read_by = None
|
||||
#: A dict with user's IDs as keys, and their :class:`MessageReaction` as values
|
||||
reactions = None
|
||||
#: The actual message
|
||||
@@ -201,6 +203,7 @@ class Message(object):
|
||||
attachments = []
|
||||
self.attachments = attachments
|
||||
self.reactions = {}
|
||||
self.read_by = []
|
||||
|
||||
def __repr__(self):
|
||||
return self.__unicode__()
|
||||
@@ -530,8 +533,8 @@ class ThreadType(Enum):
|
||||
"""Used to specify what type of Facebook thread is being used. See :ref:`intro_threads` for more info"""
|
||||
USER = 1
|
||||
GROUP = 2
|
||||
ROOM = 2
|
||||
PAGE = 3
|
||||
ROOM = 4
|
||||
|
||||
class ThreadLocation(Enum):
|
||||
"""Used to specify where a thread is located (inbox, pending, archived, other)."""
|
||||
|
Reference in New Issue
Block a user