From 5a0bcfb273b40acd0b9b8bf720809b176fc78ee6 Mon Sep 17 00:00:00 2001 From: thekindlyone Date: Sun, 28 Aug 2016 22:56:07 +0530 Subject: [PATCH 01/13] Fixed #55 , send fail due to api change. clients.py: 1. cloned data to be exact payload the js project is sending in send 2. if debug then print payload that is being sent on send utils.py : 1. added generateOfflineThreadingID and getSignatureID which are clones of the same code in js project --- fbchat/client.py | 98 ++++++++++++++++++++++++++++++++++-------------- fbchat/utils.py | 11 ++++++ 2 files changed, 81 insertions(+), 28 deletions(-) diff --git a/fbchat/client.py b/fbchat/client.py index 352a5d9..d2fea6d 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -18,7 +18,7 @@ from random import random, choice from datetime import datetime from bs4 import BeautifulSoup as bs from mimetypes import guess_type - +import math from .utils import * from .models import * from .stickers import * @@ -26,7 +26,8 @@ from .stickers import * # URLs LoginURL ="https://m.facebook.com/login.php?login_attempt=1" SearchURL ="https://www.facebook.com/ajax/typeahead/search.php" -SendURL ="https://www.facebook.com/ajax/mercury/send_messages.php" +# SendURL ="https://www.facebook.com/ajax/mercury/send_messages.php" +SendURL ="https://www.facebook.com/messaging/send/" ThreadsURL ="https://www.facebook.com/ajax/mercury/threadlist_info.php" ThreadSyncURL="https://www.facebook.com/ajax/mercury/thread_sync.php" MessagesURL ="https://www.facebook.com/ajax/mercury/thread_info.php" @@ -221,37 +222,74 @@ class Client(object): else: thread_id = None user_id = recipient_id - + # messageAndOTID=generateMessageID(self.client_id) + messageAndOTID=generateOfflineThreadingID() timestamp = now() date = datetime.now() data = { - 'client' : self.client, - 'message_batch[0][action_type]' : 'ma-type:user-generated-message', - 'message_batch[0][author]' : 'fbid:' + str(self.uid), - 'message_batch[0][specific_to_list][0]' : 'fbid:' + str(recipient_id), - 'message_batch[0][specific_to_list][1]' : 'fbid:' + str(self.uid), - 'message_batch[0][timestamp]' : timestamp, - 'message_batch[0][timestamp_absolute]' : 'Today', - 'message_batch[0][timestamp_relative]' : str(date.hour) + ":" + str(date.minute).zfill(2), - 'message_batch[0][timestamp_time_passed]' : '0', - 'message_batch[0][is_unread]' : False, - 'message_batch[0][is_cleared]' : False, - 'message_batch[0][is_forward]' : False, - 'message_batch[0][is_filtered_content]' : False, - 'message_batch[0][is_spoof_warning]' : False, - 'message_batch[0][source]' : 'source:chat:web', - 'message_batch[0][source_tags][0]' : 'source:chat', - 'message_batch[0][body]' : message, - 'message_batch[0][html_body]' : False, - 'message_batch[0][ui_push_phase]' : 'V3', - 'message_batch[0][status]' : '0', - 'message_batch[0][message_id]' : generateMessageID(self.client_id), - 'message_batch[0][manual_retry_cnt]' : '0', - 'message_batch[0][thread_fbid]' : thread_id, - 'message_batch[0][has_attachment]' : image_id != None, - 'message_batch[0][other_user_fbid]' : user_id + # 'message_batch[0][action_type]' : 'ma-type:user-generated-message', + # 'message_batch[0][author]' : 'fbid:' + str(self.uid), + # 'message_batch[0][specific_to_list][0]' : 'fbid:' + str(recipient_id), + # 'message_batch[0][specific_to_list][1]' : 'fbid:' + str(self.uid), + # 'message_batch[0][timestamp]' : timestamp, + # 'message_batch[0][timestamp_absolute]' : 'Today', + # 'message_batch[0][timestamp_relative]' : str(date.hour) + ":" + str(date.minute).zfill(2), + # 'message_batch[0][timestamp_time_passed]' : '0', + # 'message_batch[0][is_unread]' : False, + # 'message_batch[0][is_cleared]' : False, + # 'message_batch[0][is_forward]' : False, + # 'message_batch[0][is_filtered_content]' : False, + # 'message_batch[0][is_spoof_warning]' : False, + # 'message_batch[0][source]' : 'source:chat:web', + # 'message_batch[0][source_tags][0]' : 'source:chat', + # 'message_batch[0][body]' : message, + # 'message_batch[0][html_body]' : False, + # 'message_batch[0][ui_push_phase]' : 'V3', + # 'message_batch[0][status]' : '0', + # 'message_batch[0][message_id]' : generateMessageID(self.client_id), + # 'message_batch[0][manual_retry_cnt]' : '0', + # 'message_batch[0][thread_fbid]' : thread_id, + # 'message_batch[0][has_attachment]' : image_id != None, + # 'message_batch[0][other_user_fbid]' : user_id + 'client': self.client, + 'action_type' : 'ma-type:user-generated-message', + 'author' : 'fbid:' + str(self.uid), + 'timestamp' : timestamp, + 'timestamp_absolute' : 'Today', + 'timestamp_relative' : str(date.hour) + ":" + str(date.minute).zfill(2), + 'timestamp_time_passed' : '0', + 'is_unread' : False, + 'is_cleared' : False, + 'is_forward' : False, + 'is_filtered_content' : False, + 'is_filtered_content_bh': False, + 'is_filtered_content_account': False, + 'is_filtered_content_quasar': False, + 'is_filtered_content_invalid_app': False, + 'is_spoof_warning' : False, + 'source' : 'source:chat:web', + 'source_tags[0]' : 'source:chat', + 'body' : message, + 'html_body' : False, + 'ui_push_phase' : 'V3', + 'status' : '0', + 'offline_threading_id':messageAndOTID, + 'message_id' : messageAndOTID, + 'threading_id':generateMessageID(self.client_id), + 'ephemeral_ttl_mode:': '0', + 'manual_retry_cnt' : '0', + 'signatureID' : getSignatureID(), + 'has_attachment' : image_id != None, + 'other_user_fbid' : recipient_id, + 'specific_to_list[0]' : 'fbid:' + str(recipient_id), + 'specific_to_list[1]' : 'fbid:' + str(self.uid), + } + + + + if image_id: data['message_batch[0][image_ids][0]'] = image_id @@ -264,6 +302,10 @@ class Client(object): data["message_batch[0][sticker_id]"] = sticker r = self._post(SendURL, data) + if self.debug: + print r + for k,v in data.iteritems(): + print k,':',v return r.ok def sendRemoteImage(self, recipient_id, message=None, message_type='user', image=''): diff --git a/fbchat/utils.py b/fbchat/utils.py index 260a658..d8d3e65 100644 --- a/fbchat/utils.py +++ b/fbchat/utils.py @@ -34,3 +34,14 @@ def generateMessageID(client_id=None): k = now() l = int(random() * 4294967295) return ("<%s:%s-%s@mail.projektitan.com>" % (k, l, client_id)); + +def getSignatureID(): + return hex(int(random() * 2147483648)) + +def generateOfflineThreadingID() : + ret = now() + value = int(random() * 4294967295); + string = ("0000000000000000000000" + bin(value))[-22:] + msgs = bin(ret) + string + return str(int(msgs,2)) + From bb4bf36d145ddfe2388de40543396483fd342d47 Mon Sep 17 00:00:00 2001 From: thekindlyone Date: Mon, 29 Aug 2016 07:42:21 +0530 Subject: [PATCH 02/13] some cleanup. deleted commented old code --- fbchat/client.py | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/fbchat/client.py b/fbchat/client.py index d2fea6d..04c287f 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -18,7 +18,6 @@ from random import random, choice from datetime import datetime from bs4 import BeautifulSoup as bs from mimetypes import guess_type -import math from .utils import * from .models import * from .stickers import * @@ -222,35 +221,11 @@ class Client(object): else: thread_id = None user_id = recipient_id - # messageAndOTID=generateMessageID(self.client_id) + messageAndOTID=generateOfflineThreadingID() timestamp = now() date = datetime.now() data = { - # 'message_batch[0][action_type]' : 'ma-type:user-generated-message', - # 'message_batch[0][author]' : 'fbid:' + str(self.uid), - # 'message_batch[0][specific_to_list][0]' : 'fbid:' + str(recipient_id), - # 'message_batch[0][specific_to_list][1]' : 'fbid:' + str(self.uid), - # 'message_batch[0][timestamp]' : timestamp, - # 'message_batch[0][timestamp_absolute]' : 'Today', - # 'message_batch[0][timestamp_relative]' : str(date.hour) + ":" + str(date.minute).zfill(2), - # 'message_batch[0][timestamp_time_passed]' : '0', - # 'message_batch[0][is_unread]' : False, - # 'message_batch[0][is_cleared]' : False, - # 'message_batch[0][is_forward]' : False, - # 'message_batch[0][is_filtered_content]' : False, - # 'message_batch[0][is_spoof_warning]' : False, - # 'message_batch[0][source]' : 'source:chat:web', - # 'message_batch[0][source_tags][0]' : 'source:chat', - # 'message_batch[0][body]' : message, - # 'message_batch[0][html_body]' : False, - # 'message_batch[0][ui_push_phase]' : 'V3', - # 'message_batch[0][status]' : '0', - # 'message_batch[0][message_id]' : generateMessageID(self.client_id), - # 'message_batch[0][manual_retry_cnt]' : '0', - # 'message_batch[0][thread_fbid]' : thread_id, - # 'message_batch[0][has_attachment]' : image_id != None, - # 'message_batch[0][other_user_fbid]' : user_id 'client': self.client, 'action_type' : 'ma-type:user-generated-message', 'author' : 'fbid:' + str(self.uid), From e44356f265cd53b67ee2f8a5b2aa9ca296f0b50c Mon Sep 17 00:00:00 2001 From: thekindlyone Date: Mon, 29 Aug 2016 09:30:54 +0530 Subject: [PATCH 03/13] fbid in _parseMessage changed to m['delta']['messageMetadata']['actorFbId'] to fix #57 --- fbchat/client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fbchat/client.py b/fbchat/client.py index 04c287f..6215d2a 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -277,6 +277,7 @@ class Client(object): data["message_batch[0][sticker_id]"] = sticker r = self._post(SendURL, data) + if self.debug: print r for k,v in data.iteritems(): @@ -519,7 +520,7 @@ class Client(object): if 'messageMetadata' in m['delta']: mid = m['delta']['messageMetadata']['messageId'] message = m['delta']['body'] - fbid = m['delta']['messageMetadata']['threadKey']['otherUserFbId'] + fbid = m['delta']['messageMetadata']['actorFbId'] name = None self.on_message(mid, fbid, name, message, m) else: From 7ab0d3caf0de319b73648025c553eb62b7dfd672 Mon Sep 17 00:00:00 2001 From: thekindlyone Date: Mon, 29 Aug 2016 09:39:18 +0530 Subject: [PATCH 04/13] added example echobot to readme --- README.rst | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.rst b/README.rst index 0fd10fc..f2d9e13 100644 --- a/README.rst +++ b/README.rst @@ -52,6 +52,33 @@ Getting last messages sent print(message.body) +Example Echobot +=============== + +.. code-block:: python + + import fbchat + #subclass fbchat.Client and override required methods + class EchoBot(fbchat.Client): + + def __init__(self,email, password, debug=True, user_agent=None): + fbchat.Client.__init__(self,email, password, debug, user_agent) + + def on_message(self, mid, author_id, author_name, message, metadata): + self.markAsDelivered(author_id, mid) #mark delivered + self.markAsRead(author_id) #mark read + + print("%s said: %s"%(author_id, message)) + + #if you are not the author, echo + if str(author_id) != str(self.uid): + self.send(author_id,message) + + bot=EchoBot("", "") + bot.listen() + + + Authors ======= From 3025c032fe68d23f0aa7b846841fe1eeee9c0df7 Mon Sep 17 00:00:00 2001 From: thekindlyone Date: Mon, 29 Aug 2016 09:41:58 +0530 Subject: [PATCH 05/13] python 3 friendly print --- fbchat/client.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fbchat/client.py b/fbchat/client.py index 6215d2a..cd08daf 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -279,9 +279,8 @@ class Client(object): r = self._post(SendURL, data) if self.debug: - print r - for k,v in data.iteritems(): - print k,':',v + print(r) + print(data) return r.ok def sendRemoteImage(self, recipient_id, message=None, message_type='user', image=''): From 05f7b2cd4261bef49bb1a542fde8e994e1a8dc6c Mon Sep 17 00:00:00 2001 From: thekindlyone Date: Mon, 29 Aug 2016 15:03:16 +0530 Subject: [PATCH 06/13] added getUserInfo to fix #47 --- fbchat/client.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/fbchat/client.py b/fbchat/client.py index cd08daf..24d0d8a 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -25,7 +25,6 @@ from .stickers import * # URLs LoginURL ="https://m.facebook.com/login.php?login_attempt=1" SearchURL ="https://www.facebook.com/ajax/typeahead/search.php" -# SendURL ="https://www.facebook.com/ajax/mercury/send_messages.php" SendURL ="https://www.facebook.com/messaging/send/" ThreadsURL ="https://www.facebook.com/ajax/mercury/threadlist_info.php" ThreadSyncURL="https://www.facebook.com/ajax/mercury/thread_sync.php" @@ -38,7 +37,7 @@ MobileURL ="https://m.facebook.com/" StickyURL ="https://0-edge-chat.facebook.com/pull" PingURL ="https://0-channel-proxy-06-ash2.facebook.com/active_ping" UploadURL ="https://upload.facebook.com/ajax/mercury/upload.php" - +UserInfoURL ="https://www.facebook.com/chat/user_info/" class Client(object): """A client for the Facebook Chat (Messenger). @@ -548,6 +547,21 @@ class Client(object): break except requests.exceptions.Timeout: pass + + def getUserInfo(self,user_ids): + """Get user info from id. + + :param user_ids: list of or one user id(s) to query + """ + + if type(user_ids) != list: + user_ids=[user_ids] + + data = {"ids[{}]".format(i):user_id for i,user_id in enumerate(user_ids)} + r = self._post(UserInfoURL, data) + return get_json(r.text) + + def on_message(self, mid, author_id, author_name, message, metadata): From 0ac43549ed02a589a80bb750b392890a676ae60a Mon Sep 17 00:00:00 2001 From: thekindlyone Date: Mon, 29 Aug 2016 15:25:42 +0530 Subject: [PATCH 07/13] improved getUserInfo to return list of results --- fbchat/client.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/fbchat/client.py b/fbchat/client.py index 24d0d8a..d59d1f7 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -548,18 +548,16 @@ class Client(object): except requests.exceptions.Timeout: pass - def getUserInfo(self,user_ids): + def getUserInfo(self,*user_ids): """Get user info from id. - :param user_ids: list of or one user id(s) to query + :param user_ids: one or more user id(s) to query """ - if type(user_ids) != list: - user_ids=[user_ids] - data = {"ids[{}]".format(i):user_id for i,user_id in enumerate(user_ids)} r = self._post(UserInfoURL, data) - return get_json(r.text) + info = get_json(r.text) + return [details for profile,details in info['payload']['profiles'].iteritems()] From 21b38c01ebbbca6989e19a3cf5d9506ec92eecc2 Mon Sep 17 00:00:00 2001 From: thekindlyone Date: Mon, 29 Aug 2016 15:35:19 +0530 Subject: [PATCH 08/13] improved getUserInfo to return list of results or single value --- fbchat/client.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/fbchat/client.py b/fbchat/client.py index d59d1f7..650d601 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -549,7 +549,7 @@ class Client(object): pass def getUserInfo(self,*user_ids): - """Get user info from id. + """Get user info from id. Unordered. :param user_ids: one or more user id(s) to query """ @@ -557,7 +557,11 @@ class Client(object): data = {"ids[{}]".format(i):user_id for i,user_id in enumerate(user_ids)} r = self._post(UserInfoURL, data) info = get_json(r.text) - return [details for profile,details in info['payload']['profiles'].iteritems()] + full_data= [details for profile,details in info['payload']['profiles'].iteritems()] + if len(full_data)==1: + full_data=full_data[0] + return full_data + From f71b3db17831728a534967c57839c48df7af9729 Mon Sep 17 00:00:00 2001 From: thekindlyone Date: Mon, 29 Aug 2016 15:37:01 +0530 Subject: [PATCH 09/13] added getUserInfo example in readme --- README.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.rst b/README.rst index f2d9e13..bd6da0d 100644 --- a/README.rst +++ b/README.rst @@ -40,6 +40,18 @@ Sending a Message print("Message sent successfully!") +Getting user info from user id +============================== + +.. code-block:: python + + friend1 = client.getUsers('')[0] + friend2 = client.getUsers('')[0] + friend1_info = client.getUserInfo(friend1.uid) # returns dict with details + both_info = client.getUserInfo(friend1.uid,friend2.uid) # query both together, returns list of dicts + friend1_name = friend1_info['name'] + + Getting last messages sent ========================== From b0c64fad5e51196b9a65fcbaac1e0de134bdc6f8 Mon Sep 17 00:00:00 2001 From: thekindlyone Date: Tue, 30 Aug 2016 08:34:54 +0530 Subject: [PATCH 10/13] send image fixed --- fbchat/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fbchat/client.py b/fbchat/client.py index 650d601..3ef7ff4 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -265,7 +265,7 @@ class Client(object): if image_id: - data['message_batch[0][image_ids][0]'] = image_id + data['image_ids[0]'] = image_id if like: try: @@ -273,7 +273,7 @@ class Client(object): except KeyError: # if user doesn't enter l or m or s, then use the large one sticker = LIKES['l'] - data["message_batch[0][sticker_id]"] = sticker + data["sticker_id"] = sticker r = self._post(SendURL, data) From 73e6ed79592941bbee0f4db1f1185fb63051aef5 Mon Sep 17 00:00:00 2001 From: thekindlyone Date: Tue, 30 Aug 2016 08:44:25 +0530 Subject: [PATCH 11/13] readme for send image --- README.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index bd6da0d..bf978c9 100644 --- a/README.rst +++ b/README.rst @@ -38,6 +38,10 @@ Sending a Message sent = client.send(friend.uid, "Your Message") if sent: print("Message sent successfully!") + # IMAGES + client.sendLocalImage(friend.uid,message='',image='') # send local image + imgurl = "http://i.imgur.com/LDQ2ITV.jpg" + client.sendRemoteImage(friend.uid,message='', image=imgurl) # send image from image url Getting user info from user id @@ -86,7 +90,7 @@ Example Echobot if str(author_id) != str(self.uid): self.send(author_id,message) - bot=EchoBot("", "") + bot = EchoBot("", "") bot.listen() From 1a5adb5e3885ccc08d7a9b2676bf890d98bb5889 Mon Sep 17 00:00:00 2001 From: thekindlyone Date: Tue, 30 Aug 2016 13:51:43 +0530 Subject: [PATCH 12/13] issue #58, retry login feature added --- fbchat/client.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/fbchat/client.py b/fbchat/client.py index 3ef7ff4..7f49166 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -21,7 +21,7 @@ from mimetypes import guess_type from .utils import * from .models import * from .stickers import * - +import time # URLs LoginURL ="https://m.facebook.com/login.php?login_attempt=1" SearchURL ="https://www.facebook.com/ajax/typeahead/search.php" @@ -47,7 +47,7 @@ class Client(object): """ - def __init__(self, email, password, debug=True, user_agent=None): + def __init__(self, email, password, debug=True, user_agent=None , max_retries=5): """A client for the Facebook Chat (Messenger). :param email: Facebook `email` or `id` or `phone number` @@ -84,8 +84,17 @@ class Client(object): self._console("Logging in...") - if not self.login(): - raise Exception("id or password is wrong") + for i in range(1,max_retries+1): + if not self.login(): + self._console("Attempt #{} failed{}".format(i,{True:', retrying'}.get(i<5,''))) + time.sleep(1) + continue + else: + self._console("login successful") + break + else: + raise Exception("login failed. Check id/password") + self.threads = [] From b4e3b1aca33213f3a30465feb4700084a57aaafb Mon Sep 17 00:00:00 2001 From: thekindlyone Date: Thu, 1 Sep 2016 18:57:00 +0530 Subject: [PATCH 13/13] getUserInfo fixed for py3 --- fbchat/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fbchat/client.py b/fbchat/client.py index 7f49166..74ad356 100644 --- a/fbchat/client.py +++ b/fbchat/client.py @@ -566,7 +566,7 @@ class Client(object): data = {"ids[{}]".format(i):user_id for i,user_id in enumerate(user_ids)} r = self._post(UserInfoURL, data) info = get_json(r.text) - full_data= [details for profile,details in info['payload']['profiles'].iteritems()] + full_data= [details for profile,details in info['payload']['profiles'].items()] if len(full_data)==1: full_data=full_data[0] return full_data