Compare commits

..

10 Commits

Author SHA1 Message Date
Mads Marquart
b44758a195 Version up, thanks to @gave92
Fix `markAsRead` and `fetchUnread`; fixes #261
Added the `ssl_verify` instance variable, which allows disabling SSL varification for proxies
2018-03-19 21:28:48 +01:00
Mads Marquart
f1c20d490e Removed encode_params from PR, as discussed in #269 2018-03-19 21:15:23 +01:00
Mads Marquart
04372d498e Merge pull request #269 from gave92/FetchUnread
Fix `markAsRead` and `fetchUnread`; fixes #261
Added the `ssl_verify` instance variable, which allows disabling SSL varification for proxies
2018-03-19 21:08:45 +01:00
Marco Gavelli
63ea899605 fix for python3 2018-03-19 20:47:41 +01:00
Marco Gavelli
4fdd145d1e verify in _postFile 2018-03-19 16:52:22 +01:00
Marco Gavelli
57ee68b0e0 added documentation to markAsRead 2018-03-19 16:38:19 +01:00
Marco Gavelli
99c6884681 added documentation to fetchUnread 2018-03-19 16:29:26 +01:00
Marco Gavelli
1c1438e9bc fix for markAsRead, fetchUnread 2018-03-18 11:18:46 +01:00
Marco Gavelli
22f1b3e489 fix FetchUnread 2018-03-17 19:32:45 +01:00
Mads Marquart
fb1ad5800c Minor fix for searchFor. See comments on #266 2018-03-05 22:07:16 +01:00
5 changed files with 61 additions and 28 deletions

5
.gitignore vendored
View File

@@ -24,7 +24,8 @@ 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

View File

@@ -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.3' __version__ = '1.3.5'
__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'

View File

@@ -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:
@@ -137,17 +139,17 @@ class Client(object):
raise e raise e
def _cleanGet(self, url, query=None, timeout=30): def _cleanGet(self, url, query=None, timeout=30):
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)
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:
@@ -791,24 +793,34 @@ class Client(object):
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
@@ -1230,28 +1242,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)

View File

@@ -181,7 +181,9 @@ def graphql_to_thread(thread):
c_info = get_customization_info(thread) c_info = get_customization_info(thread)
participants = [node['messaging_actor'] for node in thread['all_participants']['nodes']] 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']) user = next(p for p in participants if p['id'] == thread['thread_key']['other_user_id'])
last_message_timestamp = thread['last_message']['nodes'][0]['timestamp_precise'] last_message_timestamp = None
if 'last_message' in thread:
last_message_timestamp = thread['last_message']['nodes'][0]['timestamp_precise']
return User( return User(
user['id'], user['id'],
@@ -207,7 +209,9 @@ 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 = group['last_message']['nodes'][0]['timestamp_precise'] 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']]),

View File

@@ -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"