Compare commits
38 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
38f66147cb | ||
|
ffa26c20b5 | ||
|
430ada7f84 | ||
|
988e37eb42 | ||
|
1938b90bce | ||
|
f61d1403f3 | ||
|
d228f34f64 | ||
|
97049556ed | ||
|
b64c6a94cc | ||
|
edc655bae7 | ||
|
884af48270 | ||
|
95f018fad3 | ||
|
b44758a195 | ||
|
f1c20d490e | ||
|
04372d498e | ||
|
63ea899605 | ||
|
4fdd145d1e | ||
|
57ee68b0e0 | ||
|
99c6884681 | ||
|
1c1438e9bc | ||
|
22f1b3e489 | ||
|
fb1ad5800c | ||
|
4dd15b05ef | ||
|
d7cdb644c4 | ||
|
bfcf4950b3 | ||
|
6612c97f05 | ||
|
b92cf62726 | ||
|
a53ba33a81 | ||
|
c04d38cf63 | ||
|
a051adcbc0 | ||
|
900a9cdf72 | ||
|
611b329934 | ||
|
2642788bc1 | ||
|
8268445f0b | ||
|
c12dcd9263 | ||
|
3142524809 | ||
|
4c9d3bd9d7 | ||
|
ba103066b8 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -24,7 +24,11 @@ develop-eggs
|
|||||||
# Sphinx documentation
|
# Sphinx documentation
|
||||||
docs/_build/
|
docs/_build/
|
||||||
|
|
||||||
# Data for tests
|
# Scripts and data for tests
|
||||||
|
my_tests.py
|
||||||
my_test_data.json
|
my_test_data.json
|
||||||
my_data.json
|
my_data.json
|
||||||
tests.data
|
tests.data
|
||||||
|
|
||||||
|
# Virtual environment
|
||||||
|
venv/
|
||||||
|
@@ -5,8 +5,8 @@ from fbchat import log, Client
|
|||||||
# Subclass fbchat.Client and override required methods
|
# Subclass fbchat.Client and override required methods
|
||||||
class EchoBot(Client):
|
class EchoBot(Client):
|
||||||
def onMessage(self, author_id, message_object, thread_id, thread_type, **kwargs):
|
def onMessage(self, author_id, message_object, thread_id, thread_type, **kwargs):
|
||||||
self.markAsDelivered(author_id, thread_id)
|
self.markAsDelivered(thread_id, message_object.uid)
|
||||||
self.markAsRead(author_id)
|
self.markAsRead(thread_id)
|
||||||
|
|
||||||
log.info("{} from {} in {}".format(message_object, thread_id, thread_type.name))
|
log.info("{} from {} in {}".format(message_object, thread_id, thread_type.name))
|
||||||
|
|
||||||
|
@@ -17,7 +17,7 @@ from .client import *
|
|||||||
|
|
||||||
|
|
||||||
__copyright__ = 'Copyright 2015 - {} by Taehoon Kim'.format(datetime.now().year)
|
__copyright__ = 'Copyright 2015 - {} by Taehoon Kim'.format(datetime.now().year)
|
||||||
__version__ = '1.3.0'
|
__version__ = '1.3.7'
|
||||||
__license__ = 'BSD'
|
__license__ = 'BSD'
|
||||||
__author__ = 'Taehoon Kim; Moreels Pieter-Jan; Mads Marquart'
|
__author__ = 'Taehoon Kim; Moreels Pieter-Jan; Mads Marquart'
|
||||||
__email__ = 'carpedm20@gmail.com'
|
__email__ = 'carpedm20@gmail.com'
|
||||||
|
161
fbchat/client.py
161
fbchat/client.py
@@ -20,6 +20,8 @@ class Client(object):
|
|||||||
See https://fbchat.readthedocs.io for complete documentation of the API.
|
See https://fbchat.readthedocs.io for complete documentation of the API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
ssl_verify = True
|
||||||
|
"""Verify ssl certificate, set to False to allow debugging with a proxy"""
|
||||||
listening = False
|
listening = False
|
||||||
"""Whether the client is listening. Used when creating an external event loop to determine when to stop listening"""
|
"""Whether the client is listening. Used when creating an external event loop to determine when to stop listening"""
|
||||||
uid = None
|
uid = None
|
||||||
@@ -105,7 +107,7 @@ class Client(object):
|
|||||||
|
|
||||||
def _get(self, url, query=None, timeout=30, fix_request=False, as_json=False, error_retries=3):
|
def _get(self, url, query=None, timeout=30, fix_request=False, as_json=False, error_retries=3):
|
||||||
payload = self._generatePayload(query)
|
payload = self._generatePayload(query)
|
||||||
r = self._session.get(url, headers=self._header, params=payload, timeout=timeout)
|
r = self._session.get(url, headers=self._header, params=payload, timeout=timeout, verify=self.ssl_verify)
|
||||||
if not fix_request:
|
if not fix_request:
|
||||||
return r
|
return r
|
||||||
try:
|
try:
|
||||||
@@ -117,7 +119,7 @@ class Client(object):
|
|||||||
|
|
||||||
def _post(self, url, query=None, timeout=30, fix_request=False, as_json=False, error_retries=3):
|
def _post(self, url, query=None, timeout=30, fix_request=False, as_json=False, error_retries=3):
|
||||||
payload = self._generatePayload(query)
|
payload = self._generatePayload(query)
|
||||||
r = self._session.post(url, headers=self._header, data=payload, timeout=timeout)
|
r = self._session.post(url, headers=self._header, data=payload, timeout=timeout, verify=self.ssl_verify)
|
||||||
if not fix_request:
|
if not fix_request:
|
||||||
return r
|
return r
|
||||||
try:
|
try:
|
||||||
@@ -136,18 +138,19 @@ class Client(object):
|
|||||||
return self._graphql(payload, error_retries=error_retries-1)
|
return self._graphql(payload, error_retries=error_retries-1)
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def _cleanGet(self, url, query=None, timeout=30):
|
def _cleanGet(self, url, query=None, timeout=30, allow_redirects=True):
|
||||||
return self._session.get(url, headers=self._header, params=query, timeout=timeout)
|
return self._session.get(url, headers=self._header, params=query, timeout=timeout, verify=self.ssl_verify,
|
||||||
|
allow_redirects=allow_redirects)
|
||||||
|
|
||||||
def _cleanPost(self, url, query=None, timeout=30):
|
def _cleanPost(self, url, query=None, timeout=30):
|
||||||
self.req_counter += 1
|
self.req_counter += 1
|
||||||
return self._session.post(url, headers=self._header, data=query, timeout=timeout)
|
return self._session.post(url, headers=self._header, data=query, timeout=timeout, verify=self.ssl_verify)
|
||||||
|
|
||||||
def _postFile(self, url, files=None, query=None, timeout=30, fix_request=False, as_json=False, error_retries=3):
|
def _postFile(self, url, files=None, query=None, timeout=30, fix_request=False, as_json=False, error_retries=3):
|
||||||
payload=self._generatePayload(query)
|
payload=self._generatePayload(query)
|
||||||
# Removes 'Content-Type' from the header
|
# Removes 'Content-Type' from the header
|
||||||
headers = dict((i, self._header[i]) for i in self._header if i != 'Content-Type')
|
headers = dict((i, self._header[i]) for i in self._header if i != 'Content-Type')
|
||||||
r = self._session.post(url, headers=headers, data=payload, timeout=timeout, files=files)
|
r = self._session.post(url, headers=headers, data=payload, timeout=timeout, files=files, verify=self.ssl_verify)
|
||||||
if not fix_request:
|
if not fix_request:
|
||||||
return r
|
return r
|
||||||
try:
|
try:
|
||||||
@@ -207,8 +210,18 @@ class Client(object):
|
|||||||
|
|
||||||
r = self._get(self.req_url.BASE)
|
r = self._get(self.req_url.BASE)
|
||||||
soup = bs(r.text, "lxml")
|
soup = bs(r.text, "lxml")
|
||||||
self.fb_dtsg = soup.find("input", {'name':'fb_dtsg'})['value']
|
|
||||||
self.fb_h = soup.find("input", {'name':'h'})['value']
|
fb_dtsg_element = soup.find("input", {'name': 'fb_dtsg'})
|
||||||
|
if fb_dtsg_element:
|
||||||
|
self.fb_dtsg = fb_dtsg_element['value']
|
||||||
|
else:
|
||||||
|
self.fb_dtsg = re.search(r'name="fb_dtsg" value="(.*?)"', r.text).group(1)
|
||||||
|
|
||||||
|
|
||||||
|
fb_h_element = soup.find("input", {'name':'h'})
|
||||||
|
if fb_h_element:
|
||||||
|
self.fb_h = fb_h_element['value']
|
||||||
|
|
||||||
for i in self.fb_dtsg:
|
for i in self.fb_dtsg:
|
||||||
self.ttstamp += str(ord(i))
|
self.ttstamp += str(ord(i))
|
||||||
self.ttstamp += '2'
|
self.ttstamp += '2'
|
||||||
@@ -323,8 +336,8 @@ class Client(object):
|
|||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
# Send a request to the login url, to see if we're directed to the home page
|
# Send a request to the login url, to see if we're directed to the home page
|
||||||
r = self._cleanGet(self.req_url.LOGIN)
|
r = self._cleanGet(self.req_url.LOGIN, allow_redirects=False)
|
||||||
return 'home' in r.url
|
return 'Location' in r.headers and 'home' in r.headers['Location']
|
||||||
|
|
||||||
def getSession(self):
|
def getSession(self):
|
||||||
"""Retrieves session cookies
|
"""Retrieves session cookies
|
||||||
@@ -398,6 +411,11 @@ class Client(object):
|
|||||||
:return: True if the action was successful
|
:return: True if the action was successful
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if not hasattr(self, 'fb_h'):
|
||||||
|
h_r = self._post(self.req_url.MODERN_SETTINGS_MENU, {'pmid': '4'})
|
||||||
|
self.fb_h = re.search(r'name=\\"h\\" value=\\"(.*?)\\"', h_r.text).group(1)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'ref': "mb",
|
'ref': "mb",
|
||||||
'h': self.fb_h
|
'h': self.fb_h
|
||||||
@@ -754,19 +772,23 @@ class Client(object):
|
|||||||
|
|
||||||
return list(reversed([graphql_to_message(message) for message in j['message_thread']['messages']['nodes']]))
|
return list(reversed([graphql_to_message(message) for message in j['message_thread']['messages']['nodes']]))
|
||||||
|
|
||||||
def fetchThreadList(self, offset=0, limit=20, thread_location=ThreadLocation.INBOX):
|
def fetchThreadList(self, offset=None, limit=20, thread_location=ThreadLocation.INBOX, before=None):
|
||||||
"""Get thread list of your facebook account
|
"""Get thread list of your facebook account
|
||||||
|
|
||||||
:param offset: The offset, from where in the list to recieve threads from
|
:param offset: Deprecated. Do not use!
|
||||||
:param limit: Max. number of threads to retrieve. Capped at 20
|
:param limit: Max. number of threads to retrieve. Capped at 20
|
||||||
:param thread_location: models.ThreadLocation: INBOX, PENDING, ARCHIVED or OTHER
|
:param thread_location: models.ThreadLocation: INBOX, PENDING, ARCHIVED or OTHER
|
||||||
:type offset: int
|
:param before: A timestamp (in milliseconds), indicating from which point to retrieve threads
|
||||||
:type limit: int
|
:type limit: int
|
||||||
|
:type before: int
|
||||||
:return: :class:`models.Thread` objects
|
:return: :class:`models.Thread` objects
|
||||||
:rtype: list
|
:rtype: list
|
||||||
:raises: FBchatException if request failed
|
:raises: FBchatException if request failed
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if offset is not None:
|
||||||
|
log.warning('Using `offset` in `fetchThreadList` is no longer supported, since Facebook migrated to the use of GraphQL in this request. Use `before` instead')
|
||||||
|
|
||||||
if limit > 20 or limit < 1:
|
if limit > 20 or limit < 1:
|
||||||
raise FBchatUserError('`limit` should be between 1 and 20')
|
raise FBchatUserError('`limit` should be between 1 and 20')
|
||||||
|
|
||||||
@@ -775,73 +797,46 @@ class Client(object):
|
|||||||
else:
|
else:
|
||||||
raise FBchatUserError('"thread_location" must be a value of ThreadLocation')
|
raise FBchatUserError('"thread_location" must be a value of ThreadLocation')
|
||||||
|
|
||||||
data = {
|
j = self.graphql_request(GraphQL(doc_id='1349387578499440', params={
|
||||||
'client' : self.client,
|
'limit': limit,
|
||||||
loc_str + '[offset]' : offset,
|
'tags': [loc_str],
|
||||||
loc_str + '[limit]' : limit,
|
'before': before,
|
||||||
}
|
'includeDeliveryReceipts': True,
|
||||||
|
'includeSeqID': False
|
||||||
|
}))
|
||||||
|
|
||||||
j = self._post(self.req_url.THREADS, data, fix_request=True, as_json=True)
|
return [graphql_to_thread(node) for node in j['viewer']['message_threads']['nodes']]
|
||||||
if j.get('payload') is None:
|
|
||||||
raise FBchatException('Missing payload: {}, with data: {}'.format(j, data))
|
|
||||||
|
|
||||||
participants = {}
|
|
||||||
if 'participants' in j['payload']:
|
|
||||||
for p in j['payload']['participants']:
|
|
||||||
if p['type'] == 'page':
|
|
||||||
participants[p['fbid']] = Page(p['fbid'], url=p['href'], photo=p['image_src'], name=p['name'])
|
|
||||||
elif p['type'] == 'user':
|
|
||||||
participants[p['fbid']] = User(p['fbid'], url=p['href'], first_name=p['short_name'], is_friend=p['is_friend'], gender=GENDERS.get(p['gender']), photo=p['image_src'], name=p['name'])
|
|
||||||
else:
|
|
||||||
raise FBchatException('A participant had an unknown type {}: {}'.format(p['type'], p))
|
|
||||||
|
|
||||||
entries = []
|
|
||||||
if 'threads' in j['payload']:
|
|
||||||
for k in j['payload']['threads']:
|
|
||||||
if k['thread_type'] == 1:
|
|
||||||
if k['other_user_fbid'] not in participants:
|
|
||||||
raise FBchatException('The thread {} was not in participants: {}'.format(k, j['payload']))
|
|
||||||
participants[k['other_user_fbid']].message_count = k['message_count']
|
|
||||||
entries.append(participants[k['other_user_fbid']])
|
|
||||||
elif k['thread_type'] == 2:
|
|
||||||
entries.append(Group(k['thread_fbid'], participants=set([p.strip('fbid:') for p in k['participants']]), photo=k['image_src'], name=k['name'], message_count=k['message_count']))
|
|
||||||
elif k['thread_type'] == 3:
|
|
||||||
entries.append(Room(
|
|
||||||
k['thread_fbid'],
|
|
||||||
participants = set(p.lstrip('fbid:') for p in k['participants']),
|
|
||||||
photo = k['image_src'],
|
|
||||||
name = k['name'],
|
|
||||||
message_count = k['message_count'],
|
|
||||||
admins = set(p.lstrip('fbid:') for p in k['admin_ids']),
|
|
||||||
approval_mode = k['approval_mode'],
|
|
||||||
approval_requests = set(p.lstrip('fbid:') for p in k['approval_queue_ids']),
|
|
||||||
join_link = k['joinable_mode']['link']
|
|
||||||
))
|
|
||||||
else:
|
|
||||||
raise FBchatException('A thread had an unknown thread type: {}'.format(k))
|
|
||||||
|
|
||||||
return entries
|
|
||||||
|
|
||||||
def fetchUnread(self):
|
def fetchUnread(self):
|
||||||
"""
|
"""
|
||||||
.. todo::
|
Get the unread thread list
|
||||||
Documenting this
|
|
||||||
|
|
||||||
|
:return: List of unread thread ids
|
||||||
|
:rtype: list
|
||||||
:raises: FBchatException if request failed
|
:raises: FBchatException if request failed
|
||||||
"""
|
"""
|
||||||
form = {
|
form = {
|
||||||
'client': 'mercury_sync',
|
|
||||||
'folders[0]': 'inbox',
|
'folders[0]': 'inbox',
|
||||||
|
'client': 'mercury',
|
||||||
'last_action_timestamp': now() - 60*1000
|
'last_action_timestamp': now() - 60*1000
|
||||||
# 'last_action_timestamp': 0
|
# 'last_action_timestamp': 0
|
||||||
}
|
}
|
||||||
|
|
||||||
j = self._post(self.req_url.THREAD_SYNC, form, fix_request=True, as_json=True)
|
j = self._post(self.req_url.UNREAD_THREADS, form, fix_request=True, as_json=True)
|
||||||
|
|
||||||
return {
|
return j['payload']['unread_thread_fbids'][0]['other_user_fbids']
|
||||||
"message_counts": j['payload']['message_counts'],
|
|
||||||
"unseen_threads": j['payload']['unseen_thread_ids']
|
def fetchUnseen(self):
|
||||||
}
|
"""
|
||||||
|
Get the unseen (new) thread list
|
||||||
|
|
||||||
|
:return: List of unseen thread ids
|
||||||
|
:rtype: list
|
||||||
|
:raises: FBchatException if request failed
|
||||||
|
"""
|
||||||
|
j = self._post(self.req_url.UNSEEN_THREADS, None, fix_request=True, as_json=True)
|
||||||
|
|
||||||
|
return j['payload']['unseen_thread_fbids'][0]['other_user_fbids']
|
||||||
|
|
||||||
def fetchImageUrl(self, image_id):
|
def fetchImageUrl(self, image_id):
|
||||||
"""Fetches the url to the original image from an image attachment ID
|
"""Fetches the url to the original image from an image attachment ID
|
||||||
@@ -1263,28 +1258,36 @@ class Client(object):
|
|||||||
END SEND METHODS
|
END SEND METHODS
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def markAsDelivered(self, userID, threadID):
|
def markAsDelivered(self, thread_id, message_id):
|
||||||
"""
|
"""
|
||||||
.. todo::
|
Mark a message as delivered
|
||||||
Documenting this
|
|
||||||
|
:param thread_id: User/Group ID to which the message belongs. See :ref:`intro_threads`
|
||||||
|
:param message_id: Message ID to set as delivered. See :ref:`intro_threads`
|
||||||
|
:return: Whether the request was successful
|
||||||
|
:raises: FBchatException if request failed
|
||||||
"""
|
"""
|
||||||
data = {
|
data = {
|
||||||
"message_ids[0]": threadID,
|
"message_ids[0]": message_id,
|
||||||
"thread_ids[%s][0]" % userID: threadID
|
"thread_ids[%s][0]" % thread_id: message_id
|
||||||
}
|
}
|
||||||
|
|
||||||
r = self._post(self.req_url.DELIVERED, data)
|
r = self._post(self.req_url.DELIVERED, data)
|
||||||
return r.ok
|
return r.ok
|
||||||
|
|
||||||
def markAsRead(self, userID):
|
def markAsRead(self, thread_id):
|
||||||
"""
|
"""
|
||||||
.. todo::
|
Mark a thread as read
|
||||||
Documenting this
|
All messages inside the thread will be marked as read
|
||||||
|
|
||||||
|
:param thread_id: User/Group ID to set as read. See :ref:`intro_threads`
|
||||||
|
:return: Whether the request was successful
|
||||||
|
:raises: FBchatException if request failed
|
||||||
"""
|
"""
|
||||||
data = {
|
data = {
|
||||||
|
"ids[%s]" % thread_id: 'true',
|
||||||
"watermarkTimestamp": now(),
|
"watermarkTimestamp": now(),
|
||||||
"shouldSendReadReceipt": True,
|
"shouldSendReadReceipt": 'true',
|
||||||
"ids[%s]" % userID: True
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r = self._post(self.req_url.READ_STATUS, data)
|
r = self._post(self.req_url.READ_STATUS, data)
|
||||||
@@ -1543,9 +1546,9 @@ class Client(object):
|
|||||||
#
|
#
|
||||||
# self.onSeen(m.get('realtime_viewer_fbid'), m.get('reader'), m.get('time'))
|
# self.onSeen(m.get('realtime_viewer_fbid'), m.get('reader'), m.get('time'))
|
||||||
|
|
||||||
# elif mtype in ['jewel_requests_add']:
|
elif mtype in ['jewel_requests_add']:
|
||||||
# from_id = m['from']
|
from_id = m['from']
|
||||||
# self.on_friend_request(from_id)
|
self.onFriendRequest(from_id=from_id, msg=m)
|
||||||
|
|
||||||
# Happens on every login
|
# Happens on every login
|
||||||
elif mtype == "qprimer":
|
elif mtype == "qprimer":
|
||||||
|
@@ -172,10 +172,46 @@ def graphql_to_user(user):
|
|||||||
message_count=user.get('messages_count')
|
message_count=user.get('messages_count')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def graphql_to_thread(thread):
|
||||||
|
if thread['thread_type'] == 'GROUP':
|
||||||
|
return graphql_to_group(thread)
|
||||||
|
elif thread['thread_type'] == 'ONE_TO_ONE':
|
||||||
|
if thread.get('big_image_src') is None:
|
||||||
|
thread['big_image_src'] = {}
|
||||||
|
c_info = get_customization_info(thread)
|
||||||
|
participants = [node['messaging_actor'] for node in thread['all_participants']['nodes']]
|
||||||
|
user = next(p for p in participants if p['id'] == thread['thread_key']['other_user_id'])
|
||||||
|
last_message_timestamp = None
|
||||||
|
if 'last_message' in thread:
|
||||||
|
last_message_timestamp = thread['last_message']['nodes'][0]['timestamp_precise']
|
||||||
|
|
||||||
|
return User(
|
||||||
|
user['id'],
|
||||||
|
url=user.get('url'),
|
||||||
|
name=user.get('name'),
|
||||||
|
first_name=user.get('short_name'),
|
||||||
|
last_name=user.get('name').split(user.get('short_name'),1).pop().strip(),
|
||||||
|
is_friend=user.get('is_viewer_friend'),
|
||||||
|
gender=GENDERS.get(user.get('gender')),
|
||||||
|
affinity=user.get('affinity'),
|
||||||
|
nickname=c_info.get('nickname'),
|
||||||
|
color=c_info.get('color'),
|
||||||
|
emoji=c_info.get('emoji'),
|
||||||
|
own_nickname=c_info.get('own_nickname'),
|
||||||
|
photo=user['big_image_src'].get('uri'),
|
||||||
|
message_count=thread.get('messages_count'),
|
||||||
|
last_message_timestamp=last_message_timestamp
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise FBchatException('Unknown thread type: {}, with data: {}'.format(thread.get('thread_type'), thread))
|
||||||
|
|
||||||
def graphql_to_group(group):
|
def graphql_to_group(group):
|
||||||
if group.get('image') is None:
|
if group.get('image') is None:
|
||||||
group['image'] = {}
|
group['image'] = {}
|
||||||
c_info = get_customization_info(group)
|
c_info = get_customization_info(group)
|
||||||
|
last_message_timestamp = None
|
||||||
|
if 'last_message' in group:
|
||||||
|
last_message_timestamp = group['last_message']['nodes'][0]['timestamp_precise']
|
||||||
return Group(
|
return Group(
|
||||||
group['thread_key']['thread_fbid'],
|
group['thread_key']['thread_fbid'],
|
||||||
participants=set([node['messaging_actor']['id'] for node in group['all_participants']['nodes']]),
|
participants=set([node['messaging_actor']['id'] for node in group['all_participants']['nodes']]),
|
||||||
@@ -184,7 +220,8 @@ def graphql_to_group(group):
|
|||||||
emoji=c_info.get('emoji'),
|
emoji=c_info.get('emoji'),
|
||||||
photo=group['image'].get('uri'),
|
photo=group['image'].get('uri'),
|
||||||
name=group.get('name'),
|
name=group.get('name'),
|
||||||
message_count=group.get('messages_count')
|
message_count=group.get('messages_count'),
|
||||||
|
last_message_timestamp=last_message_timestamp
|
||||||
)
|
)
|
||||||
|
|
||||||
def graphql_to_room(room):
|
def graphql_to_room(room):
|
||||||
|
@@ -451,10 +451,10 @@ class ThreadType(Enum):
|
|||||||
|
|
||||||
class ThreadLocation(Enum):
|
class ThreadLocation(Enum):
|
||||||
"""Used to specify where a thread is located (inbox, pending, archived, other)."""
|
"""Used to specify where a thread is located (inbox, pending, archived, other)."""
|
||||||
INBOX = 'inbox'
|
INBOX = 'INBOX'
|
||||||
PENDING = 'pending'
|
PENDING = 'PENDING'
|
||||||
ARCHIVED = 'action:archived'
|
ARCHIVED = 'ARCHIVED'
|
||||||
OTHER = 'other'
|
OTHER = 'OTHER'
|
||||||
|
|
||||||
class TypingStatus(Enum):
|
class TypingStatus(Enum):
|
||||||
"""Used to specify whether the user is typing or has stopped typing"""
|
"""Used to specify whether the user is typing or has stopped typing"""
|
||||||
@@ -469,7 +469,7 @@ class EmojiSize(Enum):
|
|||||||
|
|
||||||
class ThreadColor(Enum):
|
class ThreadColor(Enum):
|
||||||
"""Used to specify a thread colors"""
|
"""Used to specify a thread colors"""
|
||||||
MESSENGER_BLUE = ''
|
MESSENGER_BLUE = '#0084ff'
|
||||||
VIKING = '#44bec7'
|
VIKING = '#44bec7'
|
||||||
GOLDEN_POPPY = '#ffc300'
|
GOLDEN_POPPY = '#ffc300'
|
||||||
RADICAL_RED = '#fa3c4c'
|
RADICAL_RED = '#fa3c4c'
|
||||||
|
@@ -9,6 +9,13 @@ import warnings
|
|||||||
import logging
|
import logging
|
||||||
from .models import *
|
from .models import *
|
||||||
|
|
||||||
|
try:
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
basestring = (str, bytes)
|
||||||
|
except ImportError:
|
||||||
|
from urllib import urlencode
|
||||||
|
basestring = basestring
|
||||||
|
|
||||||
# Python 2's `input` executes the input, whereas `raw_input` just returns the input
|
# Python 2's `input` executes the input, whereas `raw_input` just returns the input
|
||||||
try:
|
try:
|
||||||
input = raw_input
|
input = raw_input
|
||||||
@@ -87,7 +94,8 @@ class ReqUrl(object):
|
|||||||
SEARCH = "https://www.facebook.com/ajax/typeahead/search.php"
|
SEARCH = "https://www.facebook.com/ajax/typeahead/search.php"
|
||||||
LOGIN = "https://m.facebook.com/login.php?login_attempt=1"
|
LOGIN = "https://m.facebook.com/login.php?login_attempt=1"
|
||||||
SEND = "https://www.facebook.com/messaging/send/"
|
SEND = "https://www.facebook.com/messaging/send/"
|
||||||
THREAD_SYNC = "https://www.facebook.com/ajax/mercury/thread_sync.php"
|
UNREAD_THREADS = "https://www.facebook.com/ajax/mercury/unread_threads.php"
|
||||||
|
UNSEEN_THREADS = "https://www.facebook.com/mercury/unseen_thread_ids/"
|
||||||
THREADS = "https://www.facebook.com/ajax/mercury/threadlist_info.php"
|
THREADS = "https://www.facebook.com/ajax/mercury/threadlist_info.php"
|
||||||
MESSAGES = "https://www.facebook.com/ajax/mercury/thread_info.php"
|
MESSAGES = "https://www.facebook.com/ajax/mercury/thread_info.php"
|
||||||
READ_STATUS = "https://www.facebook.com/ajax/mercury/change_read_status.php"
|
READ_STATUS = "https://www.facebook.com/ajax/mercury/change_read_status.php"
|
||||||
@@ -113,6 +121,7 @@ class ReqUrl(object):
|
|||||||
GRAPHQL = "https://www.facebook.com/api/graphqlbatch/"
|
GRAPHQL = "https://www.facebook.com/api/graphqlbatch/"
|
||||||
ATTACHMENT_PHOTO = "https://www.facebook.com/mercury/attachments/photo/"
|
ATTACHMENT_PHOTO = "https://www.facebook.com/mercury/attachments/photo/"
|
||||||
EVENT_REMINDER = "https://www.facebook.com/ajax/eventreminder/create"
|
EVENT_REMINDER = "https://www.facebook.com/ajax/eventreminder/create"
|
||||||
|
MODERN_SETTINGS_MENU = "https://www.facebook.com/bluebar/modern_settings_menu/"
|
||||||
|
|
||||||
pull_channel = 0
|
pull_channel = 0
|
||||||
|
|
||||||
|
2
setup.py
2
setup.py
@@ -54,10 +54,8 @@ setup(
|
|||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 2 - Pre-Alpha',
|
'Development Status :: 2 - Pre-Alpha',
|
||||||
'Intended Audience :: Developers',
|
'Intended Audience :: Developers',
|
||||||
'Intended Audience :: Developers',
|
|
||||||
'Intended Audience :: Information Technology',
|
'Intended Audience :: Information Technology',
|
||||||
'License :: OSI Approved :: BSD License',
|
'License :: OSI Approved :: BSD License',
|
||||||
'License :: OSI Approved :: BSD License',
|
|
||||||
'Operating System :: OS Independent',
|
'Operating System :: OS Independent',
|
||||||
'Programming Language :: Python :: 2.6',
|
'Programming Language :: Python :: 2.6',
|
||||||
'Programming Language :: Python :: 2.7',
|
'Programming Language :: Python :: 2.7',
|
||||||
|
5
tests.py
5
tests.py
@@ -114,7 +114,7 @@ class TestFbchat(unittest.TestCase):
|
|||||||
self.assertIsNotNone(client.send(Message(sticker=Sticker(test_sticker_id))))
|
self.assertIsNotNone(client.send(Message(sticker=Sticker(test_sticker_id))))
|
||||||
|
|
||||||
def test_sendImages(self):
|
def test_sendImages(self):
|
||||||
image_url = 'https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-image-128.png'
|
image_url = 'https://github.com/carpedm20/fbchat/raw/master/tests/image.png'
|
||||||
image_local_url = path.join(path.dirname(__file__), 'tests/image.png')
|
image_local_url = path.join(path.dirname(__file__), 'tests/image.png')
|
||||||
for thread in threads:
|
for thread in threads:
|
||||||
client.setDefaultThread(thread_id=thread['id'], thread_type=thread['type'])
|
client.setDefaultThread(thread_id=thread['id'], thread_type=thread['type'])
|
||||||
@@ -123,7 +123,8 @@ class TestFbchat(unittest.TestCase):
|
|||||||
self.assertTrue(client.sendLocalImage(image_local_url, Message(text='test_send_image_local_to__@you★', mentions=mentions)))
|
self.assertTrue(client.sendLocalImage(image_local_url, Message(text='test_send_image_local_to__@you★', mentions=mentions)))
|
||||||
|
|
||||||
def test_fetchThreadList(self):
|
def test_fetchThreadList(self):
|
||||||
client.fetchThreadList(offset=0, limit=20)
|
threads = client.fetchThreadList(limit=2)
|
||||||
|
self.assertEqual(len(threads), 2)
|
||||||
|
|
||||||
def test_fetchThreadMessages(self):
|
def test_fetchThreadMessages(self):
|
||||||
for thread in threads:
|
for thread in threads:
|
||||||
|
Reference in New Issue
Block a user