From b10b14c8e9700feb720fe2f6092d05fd60b97f67 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Sun, 30 Jun 2019 13:23:28 +0200 Subject: [PATCH 1/9] Update url in Client.removeFriend --- fbchat/_client.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/fbchat/_client.py b/fbchat/_client.py index 9c99d81..f38ffa8 100644 --- a/fbchat/_client.py +++ b/fbchat/_client.py @@ -2054,17 +2054,17 @@ class Client(object): Removes a specifed friend from your friend list :param friend_id: The ID of the friend that you want to remove - :return: Returns error if the removing was unsuccessful, returns True when successful. + :return: True + :raises: FBchatException if request failed """ - payload = {"friend_id": friend_id, "unref": "none", "confirm": "Confirm"} - r = self._post("https://m.facebook.com/a/removefriend.php", payload) - query = parse_qs(urlparse(r.url).query) - if "err" not in query: - log.debug("Remove was successful!") - return True - else: - log.warning("Error while removing friend") - return False + data = {"uid": friend_id} + j = self._post( + "/ajax/profile/removefriendconfirm.php", + data, + fix_request=True, + as_json=True, + ) + return True def blockUser(self, user_id): """ From 8d41ea5bfd53508160ee852bcb2f6676efa22b0a Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 1 Jul 2019 18:43:00 +0200 Subject: [PATCH 2/9] Use POST in Client.fetchImageUrl Reduces the amount of different request methods we're using. Not really sure whether this is actually the best option: - Each request includes `fb_dtsg` and such, so using POST everywhere might be the more secure option? - But at the same time, the request is more opaque, and harder to debug (urllib3 logs all request urls automatically, so using GET would make that easy) --- fbchat/_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fbchat/_client.py b/fbchat/_client.py index f38ffa8..c2582a5 100644 --- a/fbchat/_client.py +++ b/fbchat/_client.py @@ -944,8 +944,8 @@ class Client(object): """ image_id = str(image_id) data = {"photo_id": str(image_id)} - j = self._get( - "/mercury/attachments/photo/", query=data, fix_request=True, as_json=True + j = self._post( + "/mercury/attachments/photo/", data, fix_request=True, as_json=True ) url = get_jsmods_require(j, 3) From 50bfeb92b2bcb0e14ae40c7511be28be8497c7aa Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 1 Jul 2019 22:47:05 +0200 Subject: [PATCH 3/9] Add fix_request=True and as_json=True to missing requests I've tested, these endpoints actually all return JSON data --- fbchat/_client.py | 137 +++++++++++++++++++++++++++++++--------------- 1 file changed, 92 insertions(+), 45 deletions(-) diff --git a/fbchat/_client.py b/fbchat/_client.py index c2582a5..0f95b10 100644 --- a/fbchat/_client.py +++ b/fbchat/_client.py @@ -1251,8 +1251,9 @@ class Client(object): :param mid: :ref:`Message ID ` of the message to unsend """ data = {"message_id": mid} - r = self._post("/messaging/unsend_message/?dpr=1", data) - r.raise_for_status() + j = self._post( + "/messaging/unsend_message/?dpr=1", data, fix_request=True, as_json=True + ) def _sendLocation( self, location, current=True, message=None, thread_id=None, thread_type=None @@ -1819,7 +1820,7 @@ class Client(object): "reaction": reaction.value if reaction else None, } data = {"doc_id": 1491398900900362, "variables": json.dumps({"data": data})} - self._post("/webgraphql/mutation", data, fix_request=True, as_json=True) + j = self._post("/webgraphql/mutation", data, fix_request=True, as_json=True) def createPlan(self, plan, thread_id=None): """ @@ -1987,7 +1988,7 @@ class Client(object): :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 + :return: True :raises: FBchatException if request failed """ data = { @@ -1995,8 +1996,10 @@ class Client(object): "thread_ids[%s][0]" % thread_id: message_id, } - r = self._post("/ajax/mercury/delivery_receipts.php", data) - return r.ok + j = self._post( + "/ajax/mercury/delivery_receipts.php", data, fix_request=True, as_json=True + ) + return True def _readStatus(self, read, thread_ids): thread_ids = require_list(thread_ids) @@ -2006,8 +2009,9 @@ class Client(object): for thread_id in thread_ids: data["ids[{}]".format(thread_id)] = "true" if read else "false" - r = self._post("/ajax/mercury/change_read_status.php", data) - return r.ok + j = self._post( + "/ajax/mercury/change_read_status.php", data, fix_request=True, as_json=True + ) def markAsRead(self, thread_ids=None): """ @@ -2015,7 +2019,6 @@ class Client(object): All messages inside the threads will be marked as read :param thread_ids: User/Group IDs to set as read. See :ref:`intro_threads` - :return: Whether the request was successful :raises: FBchatException if request failed """ self._readStatus(True, thread_ids) @@ -2026,7 +2029,6 @@ class Client(object): All messages inside the threads will be marked as unread :param thread_ids: User/Group IDs to set as unread. See :ref:`intro_threads` - :return: Whether the request was successful :raises: FBchatException if request failed """ self._readStatus(False, thread_ids) @@ -2036,8 +2038,12 @@ class Client(object): .. todo:: Documenting this """ - r = self._post("/ajax/mercury/mark_seen.php", {"seen_timestamp": now()}) - return r.ok + j = self._post( + "/ajax/mercury/mark_seen.php", + {"seen_timestamp": now()}, + fix_request=True, + as_json=True, + ) def friendConnect(self, friend_id): """ @@ -2046,8 +2052,9 @@ class Client(object): """ data = {"to_friend": friend_id, "action": "confirm"} - r = self._post("/ajax/add_friend/action.php?dpr=1", data) - return r.ok + j = self._post( + "/ajax/add_friend/action.php?dpr=1", data, fix_request=True, as_json=True + ) def removeFriend(self, friend_id=None): """ @@ -2071,12 +2078,14 @@ class Client(object): Blocks messages from a specifed user :param user_id: The ID of the user that you want to block - :return: Whether the request was successful + :return: True :raises: FBchatException if request failed """ data = {"fbid": user_id} - r = self._post("/messaging/block_messages/?dpr=1", data) - return r.ok + j = self._post( + "/messaging/block_messages/?dpr=1", data, fix_request=True, as_json=True + ) + return True def unblockUser(self, user_id): """ @@ -2087,8 +2096,10 @@ class Client(object): :raises: FBchatException if request failed """ data = {"fbid": user_id} - r = self._post("/messaging/unblock_messages/?dpr=1", data) - return r.ok + j = self._post( + "/messaging/unblock_messages/?dpr=1", data, fix_request=True, as_json=True + ) + return True def moveThreads(self, location, thread_ids): """ @@ -2096,7 +2107,7 @@ class Client(object): :param location: models.ThreadLocation: INBOX, PENDING, ARCHIVED or OTHER :param thread_ids: Thread IDs to move. See :ref:`intro_threads` - :return: Whether the request was successful + :return: True :raises: FBchatException if request failed """ thread_ids = require_list(thread_ids) @@ -2110,26 +2121,33 @@ class Client(object): for thread_id in thread_ids: data_archive["ids[{}]".format(thread_id)] = "true" data_unpin["ids[{}]".format(thread_id)] = "false" - r_archive = self._post( - "/ajax/mercury/change_archived_status.php?dpr=1", data_archive + j_archive = self._post( + "/ajax/mercury/change_archived_status.php?dpr=1", + data_archive, + fix_request=True, + as_json=True, ) - r_unpin = self._post( - "/ajax/mercury/change_pinned_status.php?dpr=1", data_unpin + j_unpin = self._post( + "/ajax/mercury/change_pinned_status.php?dpr=1", + data_unpin, + fix_request=True, + as_json=True, ) - return r_archive.ok and r_unpin.ok else: data = dict() for i, thread_id in enumerate(thread_ids): data["{}[{}]".format(location.name.lower(), i)] = thread_id - r = self._post("/ajax/mercury/move_thread.php", data) - return r.ok + j = self._post( + "/ajax/mercury/move_thread.php", data, fix_request=True, as_json=True + ) + return True def deleteThreads(self, thread_ids): """ Deletes threads :param thread_ids: Thread IDs to delete. See :ref:`intro_threads` - :return: Whether the request was successful + :return: True :raises: FBchatException if request failed """ thread_ids = require_list(thread_ids) @@ -2139,36 +2157,56 @@ class Client(object): for i, thread_id in enumerate(thread_ids): data_unpin["ids[{}]".format(thread_id)] = "false" data_delete["ids[{}]".format(i)] = thread_id - r_unpin = self._post("/ajax/mercury/change_pinned_status.php?dpr=1", data_unpin) - r_delete = self._post("/ajax/mercury/delete_thread.php?dpr=1", data_delete) - return r_unpin.ok and r_delete.ok + j_unpin = self._post( + "/ajax/mercury/change_pinned_status.php?dpr=1", + data_unpin, + fix_request=True, + as_json=True, + ) + j_delete = self._post( + "/ajax/mercury/delete_thread.php?dpr=1", + data_delete, + fix_request=True, + as_json=True, + ) + return True def markAsSpam(self, thread_id=None): """ Mark a thread as spam and delete it :param thread_id: User/Group ID to mark as spam. See :ref:`intro_threads` - :return: Whether the request was successful + :return: True :raises: FBchatException if request failed """ thread_id, thread_type = self._getThread(thread_id, None) - r = self._post("/ajax/mercury/mark_spam.php?dpr=1", {"id": thread_id}) - return r.ok + j = self._post( + "/ajax/mercury/mark_spam.php?dpr=1", + {"id": thread_id}, + fix_request=True, + as_json=True, + ) + return True def deleteMessages(self, message_ids): """ Deletes specifed messages :param message_ids: Message IDs to delete - :return: Whether the request was successful + :return: True :raises: FBchatException if request failed """ message_ids = require_list(message_ids) data = dict() for i, message_id in enumerate(message_ids): data["message_ids[{}]".format(i)] = message_id - r = self._post("/ajax/mercury/delete_messages.php?dpr=1", data) - return r.ok + j = self._post( + "/ajax/mercury/delete_messages.php?dpr=1", + data, + fix_request=True, + as_json=True, + ) + return True def muteThread(self, mute_time=-1, thread_id=None): """ @@ -2179,8 +2217,11 @@ class Client(object): """ thread_id, thread_type = self._getThread(thread_id, None) data = {"mute_settings": str(mute_time), "thread_fbid": thread_id} - content = self._post( - "/ajax/mercury/change_mute_thread.php?dpr=1", data, fix_request=True + j = self._post( + "/ajax/mercury/change_mute_thread.php?dpr=1", + data, + fix_request=True, + as_json=True, ) def unmuteThread(self, thread_id=None): @@ -2200,8 +2241,11 @@ class Client(object): """ thread_id, thread_type = self._getThread(thread_id, None) data = {"reactions_mute_mode": int(mute), "thread_fbid": thread_id} - r = self._post( - "/ajax/mercury/change_reactions_mute_thread/?dpr=1", data, fix_request=True + j = self._post( + "/ajax/mercury/change_reactions_mute_thread/?dpr=1", + data, + fix_request=True, + as_json=True, ) def unmuteThreadReactions(self, thread_id=None): @@ -2221,8 +2265,11 @@ class Client(object): """ thread_id, thread_type = self._getThread(thread_id, None) data = {"mentions_mute_mode": int(mute), "thread_fbid": thread_id} - r = self._post( - "/ajax/mercury/change_mentions_mute_thread/?dpr=1", data, fix_request=True + j = self._post( + "/ajax/mercury/change_mentions_mute_thread/?dpr=1", + data, + fix_request=True, + as_json=True, ) def unmuteThreadMentions(self, thread_id=None): @@ -2250,11 +2297,11 @@ class Client(object): "viewer_uid": self._uid, "state": "active", } - self._get( + j = self._get( "https://{}-edge-chat.facebook.com/active_ping".format(self._pull_channel), data, fix_request=True, - as_json=False, + as_json=True, ) def _pullMessage(self): From b5ba338f86c372fa1662c98b6310ab678df9200a Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 1 Jul 2019 22:49:21 +0200 Subject: [PATCH 4/9] Remove fix_request parameter The requests that don't need this parameter is handled in the State model --- fbchat/_client.py | 223 ++++++++++------------------------------------ 1 file changed, 46 insertions(+), 177 deletions(-) diff --git a/fbchat/_client.py b/fbchat/_client.py index 0f95b10..682fb32 100644 --- a/fbchat/_client.py +++ b/fbchat/_client.py @@ -118,37 +118,21 @@ class Client(object): return True return False - def _get(self, url, query=None, fix_request=False, as_json=False, error_retries=3): + def _get(self, url, query=None, as_json=False, error_retries=3): payload = self._generatePayload(query) r = self._state._session.get(prefix_url(url), params=payload) - if not fix_request: - return r try: return check_request(r, as_json=as_json) except FBchatFacebookError as e: if error_retries > 0 and self._fix_fb_errors(e.fb_error_code): return self._get( - url, - query=query, - fix_request=fix_request, - as_json=as_json, - error_retries=error_retries - 1, + url, query=query, as_json=as_json, error_retries=error_retries - 1 ) raise e - def _post( - self, - url, - query=None, - fix_request=False, - as_json=False, - as_graphql=False, - error_retries=3, - ): + def _post(self, url, query=None, as_json=False, as_graphql=False, error_retries=3): payload = self._generatePayload(query) r = self._state._session.post(prefix_url(url), data=payload) - if not fix_request: - return r try: if as_graphql: content = check_request(r, as_json=False) @@ -160,26 +144,15 @@ class Client(object): return self._post( url, query=query, - fix_request=fix_request, as_json=as_json, as_graphql=as_graphql, error_retries=error_retries - 1, ) raise e - def _postFile( - self, - url, - files=None, - query=None, - fix_request=False, - as_json=False, - error_retries=3, - ): + def _postFile(self, url, files=None, query=None, as_json=False, error_retries=3): payload = self._generatePayload(query) r = self._state._session.post(prefix_url(url), data=payload, files=files) - if not fix_request: - return r try: return check_request(r, as_json=as_json) except FBchatFacebookError as e: @@ -188,7 +161,6 @@ class Client(object): url, files=files, query=query, - fix_request=fix_request, as_json=as_json, error_retries=error_retries - 1, ) @@ -208,9 +180,7 @@ class Client(object): "response_format": "json", "queries": graphql_queries_to_json(*queries), } - return tuple( - self._post("/api/graphqlbatch/", data, fix_request=True, as_graphql=True) - ) + return tuple(self._post("/api/graphqlbatch/", data, as_graphql=True)) def graphql_request(self, query): """ @@ -454,9 +424,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"viewer": self._uid} - j = self._post( - "/chat/user_info_all", query=data, fix_request=True, as_json=True - ) + j = self._post("/chat/user_info_all", query=data, as_json=True) if j.get("payload") is None: raise FBchatException("Missing payload while fetching users: {}".format(j)) @@ -568,12 +536,7 @@ class Client(object): "identifier": "thread_fbid", "thread_fbid": thread_id, } - j = self._post( - "/ajax/mercury/search_snippets.php?dpr=1", - data, - fix_request=True, - as_json=True, - ) + j = self._post("/ajax/mercury/search_snippets.php?dpr=1", data, as_json=True) result = j["payload"]["search_snippets"][query] snippets = result[thread_id]["snippets"] if result.get(thread_id) else [] @@ -618,12 +581,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"query": query, "snippetLimit": thread_limit} - j = self._post( - "/ajax/mercury/search_snippets.php?dpr=1", - data, - fix_request=True, - as_json=True, - ) + j = self._post("/ajax/mercury/search_snippets.php?dpr=1", data, as_json=True) result = j["payload"]["search_snippets"][query] if not result: @@ -641,7 +599,7 @@ class Client(object): def _fetchInfo(self, *ids): data = {"ids[{}]".format(i): _id for i, _id in enumerate(ids)} - j = self._post("/chat/user_info/", data, fix_request=True, as_json=True) + j = self._post("/chat/user_info/", data, as_json=True) if j.get("payload") is None or j["payload"].get("profiles") is None: raise FBchatException("No users/pages returned: {}".format(j)) @@ -911,9 +869,7 @@ class Client(object): "last_action_timestamp": now() - 60 * 1000 # 'last_action_timestamp': 0 } - j = self._post( - "/ajax/mercury/unread_threads.php", form, fix_request=True, as_json=True - ) + j = self._post("/ajax/mercury/unread_threads.php", form, as_json=True) payload = j["payload"]["unread_thread_fbids"][0] return payload["thread_fbids"] + payload["other_user_fbids"] @@ -926,9 +882,7 @@ class Client(object): :rtype: list :raises: FBchatException if request failed """ - j = self._post( - "/mercury/unseen_thread_ids/", None, fix_request=True, as_json=True - ) + j = self._post("/mercury/unseen_thread_ids/", None, as_json=True) payload = j["payload"]["unseen_thread_fbids"][0] return payload["thread_fbids"] + payload["other_user_fbids"] @@ -944,9 +898,7 @@ class Client(object): """ image_id = str(image_id) data = {"photo_id": str(image_id)} - j = self._post( - "/mercury/attachments/photo/", data, fix_request=True, as_json=True - ) + j = self._post("/mercury/attachments/photo/", data, as_json=True) url = get_jsmods_require(j, 3) if url is None: @@ -976,9 +928,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"question_id": poll_id} - j = self._post( - "/ajax/mercury/get_poll_options", data, fix_request=True, as_json=True - ) + j = self._post("/ajax/mercury/get_poll_options", data, as_json=True) return [PollOption._from_graphql(m) for m in j["payload"]] def fetchPlanInfo(self, plan_id): @@ -991,7 +941,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"event_reminder_id": plan_id} - j = self._post("/ajax/eventreminder", data, fix_request=True, as_json=True) + j = self._post("/ajax/eventreminder", data, as_json=True) return Plan._from_fetch(j["payload"]) def _getPrivateData(self): @@ -1116,7 +1066,7 @@ class Client(object): def _doSendRequest(self, data, get_thread_id=False): """Sends the data to `SendURL`, and returns the message ID or None on failure""" - j = self._post("/messaging/send/", data, fix_request=True, as_json=True) + j = self._post("/messaging/send/", data, as_json=True) # update JS token if received in response fb_dtsg = get_jsmods_require(j, 2) @@ -1251,9 +1201,7 @@ class Client(object): :param mid: :ref:`Message ID ` of the message to unsend """ data = {"message_id": mid} - j = self._post( - "/messaging/unsend_message/?dpr=1", data, fix_request=True, as_json=True - ) + j = self._post("/messaging/unsend_message/?dpr=1", data, as_json=True) def _sendLocation( self, location, current=True, message=None, thread_id=None, thread_type=None @@ -1331,7 +1279,6 @@ class Client(object): "https://upload.facebook.com/ajax/mercury/upload.php", files=file_dict, query=data, - fix_request=True, as_json=True, ) @@ -1511,9 +1458,7 @@ class Client(object): "attachment_id": attachment_id, "recipient_map[{}]".format(generateOfflineThreadingID()): thread_id, } - j = self._post( - "/mercury/attachments/forward/", data, fix_request=True, as_json=True - ) + j = self._post("/mercury/attachments/forward/", data, as_json=True) def createGroup(self, message, user_ids): """ @@ -1579,9 +1524,7 @@ class Client(object): thread_id, thread_type = self._getThread(thread_id, None) data = {"uid": user_id, "tid": thread_id} - j = self._post( - "/chat/remove_participants/", data, fix_request=True, as_json=True - ) + j = self._post("/chat/remove_participants/", data, as_json=True) def _adminStatus(self, admin_ids, admin, thread_id=None): thread_id, thread_type = self._getThread(thread_id, None) @@ -1593,9 +1536,7 @@ class Client(object): for i, admin_id in enumerate(admin_ids): data["admin_ids[{}]".format(i)] = str(admin_id) - j = self._post( - "/messaging/save_admins/?dpr=1", data, fix_request=True, as_json=True - ) + j = self._post("/messaging/save_admins/?dpr=1", data, as_json=True) def addGroupAdmins(self, admin_ids, thread_id=None): """ @@ -1628,9 +1569,7 @@ class Client(object): thread_id, thread_type = self._getThread(thread_id, None) data = {"set_mode": int(require_admin_approval), "thread_fbid": thread_id} - j = self._post( - "/messaging/set_approval_mode/?dpr=1", data, fix_request=True, as_json=True - ) + j = self._post("/messaging/set_approval_mode/?dpr=1", data, as_json=True) def _usersApproval(self, user_ids, approve, thread_id=None): thread_id, thread_type = self._getThread(thread_id, None) @@ -1681,9 +1620,7 @@ class Client(object): data = {"thread_image_id": image_id, "thread_id": thread_id} - j = self._post( - "/messaging/set_thread_image/?dpr=1", data, fix_request=True, as_json=True - ) + j = self._post("/messaging/set_thread_image/?dpr=1", data, as_json=True) return image_id def changeGroupImageRemote(self, image_url, thread_id=None): @@ -1730,9 +1667,7 @@ class Client(object): ) data = {"thread_name": title, "thread_id": thread_id} - j = self._post( - "/messaging/set_thread_name/?dpr=1", data, fix_request=True, as_json=True - ) + j = self._post("/messaging/set_thread_name/?dpr=1", data, as_json=True) def changeNickname( self, nickname, user_id, thread_id=None, thread_type=ThreadType.USER @@ -1757,7 +1692,6 @@ class Client(object): j = self._post( "/messaging/save_thread_nickname/?source=thread_settings&dpr=1", data, - fix_request=True, as_json=True, ) @@ -1779,7 +1713,6 @@ class Client(object): j = self._post( "/messaging/save_thread_color/?source=thread_settings&dpr=1", data, - fix_request=True, as_json=True, ) @@ -1799,7 +1732,6 @@ class Client(object): j = self._post( "/messaging/save_thread_emoji/?source=thread_settings&dpr=1", data, - fix_request=True, as_json=True, ) @@ -1820,7 +1752,7 @@ class Client(object): "reaction": reaction.value if reaction else None, } data = {"doc_id": 1491398900900362, "variables": json.dumps({"data": data})} - j = self._post("/webgraphql/mutation", data, fix_request=True, as_json=True) + j = self._post("/webgraphql/mutation", data, as_json=True) def createPlan(self, plan, thread_id=None): """ @@ -1842,9 +1774,7 @@ class Client(object): "location_name": plan.location or "", "acontext": ACONTEXT, } - j = self._post( - "/ajax/eventreminder/create", data, fix_request=True, as_json=True - ) + j = self._post("/ajax/eventreminder/create", data, as_json=True) def editPlan(self, plan, new_plan): """ @@ -1864,9 +1794,7 @@ class Client(object): "title": new_plan.title, "acontext": ACONTEXT, } - j = self._post( - "/ajax/eventreminder/submit", data, fix_request=True, as_json=True - ) + j = self._post("/ajax/eventreminder/submit", data, as_json=True) def deletePlan(self, plan): """ @@ -1876,9 +1804,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"event_reminder_id": plan.uid, "delete": "true", "acontext": ACONTEXT} - j = self._post( - "/ajax/eventreminder/submit", data, fix_request=True, as_json=True - ) + j = self._post("/ajax/eventreminder/submit", data, as_json=True) def changePlanParticipation(self, plan, take_part=True): """ @@ -1893,7 +1819,7 @@ class Client(object): "guest_state": "GOING" if take_part else "DECLINED", "acontext": ACONTEXT, } - j = self._post("/ajax/eventreminder/rsvp", data, fix_request=True, as_json=True) + j = self._post("/ajax/eventreminder/rsvp", data, as_json=True) def eventReminder(self, thread_id, time, title, location="", location_id=""): """ @@ -1924,10 +1850,7 @@ class Client(object): data["option_is_selected_array[{}]".format(i)] = str(int(option.vote)) j = self._post( - "/messaging/group_polling/create_poll/?dpr=1", - data, - fix_request=True, - as_json=True, + "/messaging/group_polling/create_poll/?dpr=1", data, as_json=True ) def updatePollVote(self, poll_id, option_ids=[], new_options=[]): @@ -1951,10 +1874,7 @@ class Client(object): data["new_options[{}]".format(i)] = option_text j = self._post( - "/messaging/group_polling/update_vote/?dpr=1", - data, - fix_request=True, - as_json=True, + "/messaging/group_polling/update_vote/?dpr=1", data, as_json=True ) def setTypingStatus(self, status, thread_id=None, thread_type=None): @@ -1976,7 +1896,7 @@ class Client(object): "to": thread_id if thread_type == ThreadType.USER else "", "source": "mercury-chat", } - j = self._post("/ajax/messaging/typ.php", data, fix_request=True, as_json=True) + j = self._post("/ajax/messaging/typ.php", data, as_json=True) """ END SEND METHODS @@ -1996,9 +1916,7 @@ class Client(object): "thread_ids[%s][0]" % thread_id: message_id, } - j = self._post( - "/ajax/mercury/delivery_receipts.php", data, fix_request=True, as_json=True - ) + j = self._post("/ajax/mercury/delivery_receipts.php", data, as_json=True) return True def _readStatus(self, read, thread_ids): @@ -2009,9 +1927,7 @@ class Client(object): for thread_id in thread_ids: data["ids[{}]".format(thread_id)] = "true" if read else "false" - j = self._post( - "/ajax/mercury/change_read_status.php", data, fix_request=True, as_json=True - ) + j = self._post("/ajax/mercury/change_read_status.php", data, as_json=True) def markAsRead(self, thread_ids=None): """ @@ -2039,10 +1955,7 @@ class Client(object): Documenting this """ j = self._post( - "/ajax/mercury/mark_seen.php", - {"seen_timestamp": now()}, - fix_request=True, - as_json=True, + "/ajax/mercury/mark_seen.php", {"seen_timestamp": now()}, as_json=True ) def friendConnect(self, friend_id): @@ -2052,9 +1965,7 @@ class Client(object): """ data = {"to_friend": friend_id, "action": "confirm"} - j = self._post( - "/ajax/add_friend/action.php?dpr=1", data, fix_request=True, as_json=True - ) + j = self._post("/ajax/add_friend/action.php?dpr=1", data, as_json=True) def removeFriend(self, friend_id=None): """ @@ -2065,12 +1976,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"uid": friend_id} - j = self._post( - "/ajax/profile/removefriendconfirm.php", - data, - fix_request=True, - as_json=True, - ) + j = self._post("/ajax/profile/removefriendconfirm.php", data, as_json=True) return True def blockUser(self, user_id): @@ -2082,9 +1988,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"fbid": user_id} - j = self._post( - "/messaging/block_messages/?dpr=1", data, fix_request=True, as_json=True - ) + j = self._post("/messaging/block_messages/?dpr=1", data, as_json=True) return True def unblockUser(self, user_id): @@ -2096,9 +2000,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"fbid": user_id} - j = self._post( - "/messaging/unblock_messages/?dpr=1", data, fix_request=True, as_json=True - ) + j = self._post("/messaging/unblock_messages/?dpr=1", data, as_json=True) return True def moveThreads(self, location, thread_ids): @@ -2124,22 +2026,16 @@ class Client(object): j_archive = self._post( "/ajax/mercury/change_archived_status.php?dpr=1", data_archive, - fix_request=True, as_json=True, ) j_unpin = self._post( - "/ajax/mercury/change_pinned_status.php?dpr=1", - data_unpin, - fix_request=True, - as_json=True, + "/ajax/mercury/change_pinned_status.php?dpr=1", data_unpin, as_json=True ) else: data = dict() for i, thread_id in enumerate(thread_ids): data["{}[{}]".format(location.name.lower(), i)] = thread_id - j = self._post( - "/ajax/mercury/move_thread.php", data, fix_request=True, as_json=True - ) + j = self._post("/ajax/mercury/move_thread.php", data, as_json=True) return True def deleteThreads(self, thread_ids): @@ -2158,16 +2054,10 @@ class Client(object): data_unpin["ids[{}]".format(thread_id)] = "false" data_delete["ids[{}]".format(i)] = thread_id j_unpin = self._post( - "/ajax/mercury/change_pinned_status.php?dpr=1", - data_unpin, - fix_request=True, - as_json=True, + "/ajax/mercury/change_pinned_status.php?dpr=1", data_unpin, as_json=True ) j_delete = self._post( - "/ajax/mercury/delete_thread.php?dpr=1", - data_delete, - fix_request=True, - as_json=True, + "/ajax/mercury/delete_thread.php?dpr=1", data_delete, as_json=True ) return True @@ -2181,10 +2071,7 @@ class Client(object): """ thread_id, thread_type = self._getThread(thread_id, None) j = self._post( - "/ajax/mercury/mark_spam.php?dpr=1", - {"id": thread_id}, - fix_request=True, - as_json=True, + "/ajax/mercury/mark_spam.php?dpr=1", {"id": thread_id}, as_json=True ) return True @@ -2200,12 +2087,7 @@ class Client(object): data = dict() for i, message_id in enumerate(message_ids): data["message_ids[{}]".format(i)] = message_id - j = self._post( - "/ajax/mercury/delete_messages.php?dpr=1", - data, - fix_request=True, - as_json=True, - ) + j = self._post("/ajax/mercury/delete_messages.php?dpr=1", data, as_json=True) return True def muteThread(self, mute_time=-1, thread_id=None): @@ -2217,12 +2099,7 @@ class Client(object): """ thread_id, thread_type = self._getThread(thread_id, None) data = {"mute_settings": str(mute_time), "thread_fbid": thread_id} - j = self._post( - "/ajax/mercury/change_mute_thread.php?dpr=1", - data, - fix_request=True, - as_json=True, - ) + j = self._post("/ajax/mercury/change_mute_thread.php?dpr=1", data, as_json=True) def unmuteThread(self, thread_id=None): """ @@ -2242,10 +2119,7 @@ class Client(object): thread_id, thread_type = self._getThread(thread_id, None) data = {"reactions_mute_mode": int(mute), "thread_fbid": thread_id} j = self._post( - "/ajax/mercury/change_reactions_mute_thread/?dpr=1", - data, - fix_request=True, - as_json=True, + "/ajax/mercury/change_reactions_mute_thread/?dpr=1", data, as_json=True ) def unmuteThreadReactions(self, thread_id=None): @@ -2266,10 +2140,7 @@ class Client(object): thread_id, thread_type = self._getThread(thread_id, None) data = {"mentions_mute_mode": int(mute), "thread_fbid": thread_id} j = self._post( - "/ajax/mercury/change_mentions_mute_thread/?dpr=1", - data, - fix_request=True, - as_json=True, + "/ajax/mercury/change_mentions_mute_thread/?dpr=1", data, as_json=True ) def unmuteThreadMentions(self, thread_id=None): @@ -2300,7 +2171,6 @@ class Client(object): j = self._get( "https://{}-edge-chat.facebook.com/active_ping".format(self._pull_channel), data, - fix_request=True, as_json=True, ) @@ -2317,7 +2187,6 @@ class Client(object): return self._get( "https://{}-edge-chat.facebook.com/pull".format(self._pull_channel), data, - fix_request=True, as_json=True, ) From 7af83c04c0bfb683642b9670c5cb2d4875ad8313 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 1 Jul 2019 22:53:26 +0200 Subject: [PATCH 5/9] Remove as_json parameter The requests that didn't need this parameter were moved to the State model --- fbchat/_client.py | 143 +++++++++++++++++----------------------------- 1 file changed, 54 insertions(+), 89 deletions(-) diff --git a/fbchat/_client.py b/fbchat/_client.py index 682fb32..ef0e4e0 100644 --- a/fbchat/_client.py +++ b/fbchat/_client.py @@ -118,19 +118,17 @@ class Client(object): return True return False - def _get(self, url, query=None, as_json=False, error_retries=3): + def _get(self, url, query=None, error_retries=3): payload = self._generatePayload(query) r = self._state._session.get(prefix_url(url), params=payload) try: - return check_request(r, as_json=as_json) + return check_request(r) except FBchatFacebookError as e: if error_retries > 0 and self._fix_fb_errors(e.fb_error_code): - return self._get( - url, query=query, as_json=as_json, error_retries=error_retries - 1 - ) + return self._get(url, query=query, error_retries=error_retries - 1) raise e - def _post(self, url, query=None, as_json=False, as_graphql=False, error_retries=3): + def _post(self, url, query=None, as_graphql=False, error_retries=3): payload = self._generatePayload(query) r = self._state._session.post(prefix_url(url), data=payload) try: @@ -138,31 +136,26 @@ class Client(object): content = check_request(r, as_json=False) return graphql_response_to_json(content) else: - return check_request(r, as_json=as_json) + return check_request(r) except FBchatFacebookError as e: if error_retries > 0 and self._fix_fb_errors(e.fb_error_code): return self._post( url, query=query, - as_json=as_json, as_graphql=as_graphql, error_retries=error_retries - 1, ) raise e - def _postFile(self, url, files=None, query=None, as_json=False, error_retries=3): + def _postFile(self, url, files=None, query=None, error_retries=3): payload = self._generatePayload(query) r = self._state._session.post(prefix_url(url), data=payload, files=files) try: - return check_request(r, as_json=as_json) + return check_request(r) except FBchatFacebookError as e: if error_retries > 0 and self._fix_fb_errors(e.fb_error_code): return self._postFile( - url, - files=files, - query=query, - as_json=as_json, - error_retries=error_retries - 1, + url, files=files, query=query, error_retries=error_retries - 1 ) raise e @@ -424,7 +417,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"viewer": self._uid} - j = self._post("/chat/user_info_all", query=data, as_json=True) + j = self._post("/chat/user_info_all", query=data) if j.get("payload") is None: raise FBchatException("Missing payload while fetching users: {}".format(j)) @@ -536,7 +529,7 @@ class Client(object): "identifier": "thread_fbid", "thread_fbid": thread_id, } - j = self._post("/ajax/mercury/search_snippets.php?dpr=1", data, as_json=True) + j = self._post("/ajax/mercury/search_snippets.php?dpr=1", data) result = j["payload"]["search_snippets"][query] snippets = result[thread_id]["snippets"] if result.get(thread_id) else [] @@ -581,7 +574,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"query": query, "snippetLimit": thread_limit} - j = self._post("/ajax/mercury/search_snippets.php?dpr=1", data, as_json=True) + j = self._post("/ajax/mercury/search_snippets.php?dpr=1", data) result = j["payload"]["search_snippets"][query] if not result: @@ -599,7 +592,7 @@ class Client(object): def _fetchInfo(self, *ids): data = {"ids[{}]".format(i): _id for i, _id in enumerate(ids)} - j = self._post("/chat/user_info/", data, as_json=True) + j = self._post("/chat/user_info/", data) if j.get("payload") is None or j["payload"].get("profiles") is None: raise FBchatException("No users/pages returned: {}".format(j)) @@ -869,7 +862,7 @@ class Client(object): "last_action_timestamp": now() - 60 * 1000 # 'last_action_timestamp': 0 } - j = self._post("/ajax/mercury/unread_threads.php", form, as_json=True) + j = self._post("/ajax/mercury/unread_threads.php", form) payload = j["payload"]["unread_thread_fbids"][0] return payload["thread_fbids"] + payload["other_user_fbids"] @@ -882,7 +875,7 @@ class Client(object): :rtype: list :raises: FBchatException if request failed """ - j = self._post("/mercury/unseen_thread_ids/", None, as_json=True) + j = self._post("/mercury/unseen_thread_ids/", None) payload = j["payload"]["unseen_thread_fbids"][0] return payload["thread_fbids"] + payload["other_user_fbids"] @@ -898,7 +891,7 @@ class Client(object): """ image_id = str(image_id) data = {"photo_id": str(image_id)} - j = self._post("/mercury/attachments/photo/", data, as_json=True) + j = self._post("/mercury/attachments/photo/", data) url = get_jsmods_require(j, 3) if url is None: @@ -928,7 +921,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"question_id": poll_id} - j = self._post("/ajax/mercury/get_poll_options", data, as_json=True) + j = self._post("/ajax/mercury/get_poll_options", data) return [PollOption._from_graphql(m) for m in j["payload"]] def fetchPlanInfo(self, plan_id): @@ -941,7 +934,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"event_reminder_id": plan_id} - j = self._post("/ajax/eventreminder", data, as_json=True) + j = self._post("/ajax/eventreminder", data) return Plan._from_fetch(j["payload"]) def _getPrivateData(self): @@ -1066,7 +1059,7 @@ class Client(object): def _doSendRequest(self, data, get_thread_id=False): """Sends the data to `SendURL`, and returns the message ID or None on failure""" - j = self._post("/messaging/send/", data, as_json=True) + j = self._post("/messaging/send/", data) # update JS token if received in response fb_dtsg = get_jsmods_require(j, 2) @@ -1201,7 +1194,7 @@ class Client(object): :param mid: :ref:`Message ID ` of the message to unsend """ data = {"message_id": mid} - j = self._post("/messaging/unsend_message/?dpr=1", data, as_json=True) + j = self._post("/messaging/unsend_message/?dpr=1", data) def _sendLocation( self, location, current=True, message=None, thread_id=None, thread_type=None @@ -1279,7 +1272,6 @@ class Client(object): "https://upload.facebook.com/ajax/mercury/upload.php", files=file_dict, query=data, - as_json=True, ) if len(j["payload"]["metadata"]) != len(files): @@ -1458,7 +1450,7 @@ class Client(object): "attachment_id": attachment_id, "recipient_map[{}]".format(generateOfflineThreadingID()): thread_id, } - j = self._post("/mercury/attachments/forward/", data, as_json=True) + j = self._post("/mercury/attachments/forward/", data) def createGroup(self, message, user_ids): """ @@ -1524,7 +1516,7 @@ class Client(object): thread_id, thread_type = self._getThread(thread_id, None) data = {"uid": user_id, "tid": thread_id} - j = self._post("/chat/remove_participants/", data, as_json=True) + j = self._post("/chat/remove_participants/", data) def _adminStatus(self, admin_ids, admin, thread_id=None): thread_id, thread_type = self._getThread(thread_id, None) @@ -1536,7 +1528,7 @@ class Client(object): for i, admin_id in enumerate(admin_ids): data["admin_ids[{}]".format(i)] = str(admin_id) - j = self._post("/messaging/save_admins/?dpr=1", data, as_json=True) + j = self._post("/messaging/save_admins/?dpr=1", data) def addGroupAdmins(self, admin_ids, thread_id=None): """ @@ -1569,7 +1561,7 @@ class Client(object): thread_id, thread_type = self._getThread(thread_id, None) data = {"set_mode": int(require_admin_approval), "thread_fbid": thread_id} - j = self._post("/messaging/set_approval_mode/?dpr=1", data, as_json=True) + j = self._post("/messaging/set_approval_mode/?dpr=1", data) def _usersApproval(self, user_ids, approve, thread_id=None): thread_id, thread_type = self._getThread(thread_id, None) @@ -1620,7 +1612,7 @@ class Client(object): data = {"thread_image_id": image_id, "thread_id": thread_id} - j = self._post("/messaging/set_thread_image/?dpr=1", data, as_json=True) + j = self._post("/messaging/set_thread_image/?dpr=1", data) return image_id def changeGroupImageRemote(self, image_url, thread_id=None): @@ -1667,7 +1659,7 @@ class Client(object): ) data = {"thread_name": title, "thread_id": thread_id} - j = self._post("/messaging/set_thread_name/?dpr=1", data, as_json=True) + j = self._post("/messaging/set_thread_name/?dpr=1", data) def changeNickname( self, nickname, user_id, thread_id=None, thread_type=ThreadType.USER @@ -1690,9 +1682,7 @@ class Client(object): "thread_or_other_fbid": thread_id, } j = self._post( - "/messaging/save_thread_nickname/?source=thread_settings&dpr=1", - data, - as_json=True, + "/messaging/save_thread_nickname/?source=thread_settings&dpr=1", data ) def changeThreadColor(self, color, thread_id=None): @@ -1711,9 +1701,7 @@ class Client(object): "thread_or_other_fbid": thread_id, } j = self._post( - "/messaging/save_thread_color/?source=thread_settings&dpr=1", - data, - as_json=True, + "/messaging/save_thread_color/?source=thread_settings&dpr=1", data ) def changeThreadEmoji(self, emoji, thread_id=None): @@ -1730,9 +1718,7 @@ class Client(object): data = {"emoji_choice": emoji, "thread_or_other_fbid": thread_id} j = self._post( - "/messaging/save_thread_emoji/?source=thread_settings&dpr=1", - data, - as_json=True, + "/messaging/save_thread_emoji/?source=thread_settings&dpr=1", data ) def reactToMessage(self, message_id, reaction): @@ -1752,7 +1738,7 @@ class Client(object): "reaction": reaction.value if reaction else None, } data = {"doc_id": 1491398900900362, "variables": json.dumps({"data": data})} - j = self._post("/webgraphql/mutation", data, as_json=True) + j = self._post("/webgraphql/mutation", data) def createPlan(self, plan, thread_id=None): """ @@ -1774,7 +1760,7 @@ class Client(object): "location_name": plan.location or "", "acontext": ACONTEXT, } - j = self._post("/ajax/eventreminder/create", data, as_json=True) + j = self._post("/ajax/eventreminder/create", data) def editPlan(self, plan, new_plan): """ @@ -1794,7 +1780,7 @@ class Client(object): "title": new_plan.title, "acontext": ACONTEXT, } - j = self._post("/ajax/eventreminder/submit", data, as_json=True) + j = self._post("/ajax/eventreminder/submit", data) def deletePlan(self, plan): """ @@ -1804,7 +1790,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"event_reminder_id": plan.uid, "delete": "true", "acontext": ACONTEXT} - j = self._post("/ajax/eventreminder/submit", data, as_json=True) + j = self._post("/ajax/eventreminder/submit", data) def changePlanParticipation(self, plan, take_part=True): """ @@ -1819,7 +1805,7 @@ class Client(object): "guest_state": "GOING" if take_part else "DECLINED", "acontext": ACONTEXT, } - j = self._post("/ajax/eventreminder/rsvp", data, as_json=True) + j = self._post("/ajax/eventreminder/rsvp", data) def eventReminder(self, thread_id, time, title, location="", location_id=""): """ @@ -1849,9 +1835,7 @@ class Client(object): data["option_text_array[{}]".format(i)] = option.text data["option_is_selected_array[{}]".format(i)] = str(int(option.vote)) - j = self._post( - "/messaging/group_polling/create_poll/?dpr=1", data, as_json=True - ) + j = self._post("/messaging/group_polling/create_poll/?dpr=1", data) def updatePollVote(self, poll_id, option_ids=[], new_options=[]): """ @@ -1873,9 +1857,7 @@ class Client(object): for i, option_text in enumerate(new_options): data["new_options[{}]".format(i)] = option_text - j = self._post( - "/messaging/group_polling/update_vote/?dpr=1", data, as_json=True - ) + j = self._post("/messaging/group_polling/update_vote/?dpr=1", data) def setTypingStatus(self, status, thread_id=None, thread_type=None): """ @@ -1896,7 +1878,7 @@ class Client(object): "to": thread_id if thread_type == ThreadType.USER else "", "source": "mercury-chat", } - j = self._post("/ajax/messaging/typ.php", data, as_json=True) + j = self._post("/ajax/messaging/typ.php", data) """ END SEND METHODS @@ -1916,7 +1898,7 @@ class Client(object): "thread_ids[%s][0]" % thread_id: message_id, } - j = self._post("/ajax/mercury/delivery_receipts.php", data, as_json=True) + j = self._post("/ajax/mercury/delivery_receipts.php", data) return True def _readStatus(self, read, thread_ids): @@ -1927,7 +1909,7 @@ class Client(object): for thread_id in thread_ids: data["ids[{}]".format(thread_id)] = "true" if read else "false" - j = self._post("/ajax/mercury/change_read_status.php", data, as_json=True) + j = self._post("/ajax/mercury/change_read_status.php", data) def markAsRead(self, thread_ids=None): """ @@ -1954,9 +1936,7 @@ class Client(object): .. todo:: Documenting this """ - j = self._post( - "/ajax/mercury/mark_seen.php", {"seen_timestamp": now()}, as_json=True - ) + j = self._post("/ajax/mercury/mark_seen.php", {"seen_timestamp": now()}) def friendConnect(self, friend_id): """ @@ -1965,7 +1945,7 @@ class Client(object): """ data = {"to_friend": friend_id, "action": "confirm"} - j = self._post("/ajax/add_friend/action.php?dpr=1", data, as_json=True) + j = self._post("/ajax/add_friend/action.php?dpr=1", data) def removeFriend(self, friend_id=None): """ @@ -1976,7 +1956,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"uid": friend_id} - j = self._post("/ajax/profile/removefriendconfirm.php", data, as_json=True) + j = self._post("/ajax/profile/removefriendconfirm.php", data) return True def blockUser(self, user_id): @@ -1988,7 +1968,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"fbid": user_id} - j = self._post("/messaging/block_messages/?dpr=1", data, as_json=True) + j = self._post("/messaging/block_messages/?dpr=1", data) return True def unblockUser(self, user_id): @@ -2000,7 +1980,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"fbid": user_id} - j = self._post("/messaging/unblock_messages/?dpr=1", data, as_json=True) + j = self._post("/messaging/unblock_messages/?dpr=1", data) return True def moveThreads(self, location, thread_ids): @@ -2024,18 +2004,16 @@ class Client(object): data_archive["ids[{}]".format(thread_id)] = "true" data_unpin["ids[{}]".format(thread_id)] = "false" j_archive = self._post( - "/ajax/mercury/change_archived_status.php?dpr=1", - data_archive, - as_json=True, + "/ajax/mercury/change_archived_status.php?dpr=1", data_archive ) j_unpin = self._post( - "/ajax/mercury/change_pinned_status.php?dpr=1", data_unpin, as_json=True + "/ajax/mercury/change_pinned_status.php?dpr=1", data_unpin ) else: data = dict() for i, thread_id in enumerate(thread_ids): data["{}[{}]".format(location.name.lower(), i)] = thread_id - j = self._post("/ajax/mercury/move_thread.php", data, as_json=True) + j = self._post("/ajax/mercury/move_thread.php", data) return True def deleteThreads(self, thread_ids): @@ -2053,12 +2031,8 @@ class Client(object): for i, thread_id in enumerate(thread_ids): data_unpin["ids[{}]".format(thread_id)] = "false" data_delete["ids[{}]".format(i)] = thread_id - j_unpin = self._post( - "/ajax/mercury/change_pinned_status.php?dpr=1", data_unpin, as_json=True - ) - j_delete = self._post( - "/ajax/mercury/delete_thread.php?dpr=1", data_delete, as_json=True - ) + j_unpin = self._post("/ajax/mercury/change_pinned_status.php?dpr=1", data_unpin) + j_delete = self._post("/ajax/mercury/delete_thread.php?dpr=1", data_delete) return True def markAsSpam(self, thread_id=None): @@ -2070,9 +2044,7 @@ class Client(object): :raises: FBchatException if request failed """ thread_id, thread_type = self._getThread(thread_id, None) - j = self._post( - "/ajax/mercury/mark_spam.php?dpr=1", {"id": thread_id}, as_json=True - ) + j = self._post("/ajax/mercury/mark_spam.php?dpr=1", {"id": thread_id}) return True def deleteMessages(self, message_ids): @@ -2087,7 +2059,7 @@ class Client(object): data = dict() for i, message_id in enumerate(message_ids): data["message_ids[{}]".format(i)] = message_id - j = self._post("/ajax/mercury/delete_messages.php?dpr=1", data, as_json=True) + j = self._post("/ajax/mercury/delete_messages.php?dpr=1", data) return True def muteThread(self, mute_time=-1, thread_id=None): @@ -2099,7 +2071,7 @@ class Client(object): """ thread_id, thread_type = self._getThread(thread_id, None) data = {"mute_settings": str(mute_time), "thread_fbid": thread_id} - j = self._post("/ajax/mercury/change_mute_thread.php?dpr=1", data, as_json=True) + j = self._post("/ajax/mercury/change_mute_thread.php?dpr=1", data) def unmuteThread(self, thread_id=None): """ @@ -2118,9 +2090,7 @@ class Client(object): """ thread_id, thread_type = self._getThread(thread_id, None) data = {"reactions_mute_mode": int(mute), "thread_fbid": thread_id} - j = self._post( - "/ajax/mercury/change_reactions_mute_thread/?dpr=1", data, as_json=True - ) + j = self._post("/ajax/mercury/change_reactions_mute_thread/?dpr=1", data) def unmuteThreadReactions(self, thread_id=None): """ @@ -2139,9 +2109,7 @@ class Client(object): """ thread_id, thread_type = self._getThread(thread_id, None) data = {"mentions_mute_mode": int(mute), "thread_fbid": thread_id} - j = self._post( - "/ajax/mercury/change_mentions_mute_thread/?dpr=1", data, as_json=True - ) + j = self._post("/ajax/mercury/change_mentions_mute_thread/?dpr=1", data) def unmuteThreadMentions(self, thread_id=None): """ @@ -2171,7 +2139,6 @@ class Client(object): j = self._get( "https://{}-edge-chat.facebook.com/active_ping".format(self._pull_channel), data, - as_json=True, ) def _pullMessage(self): @@ -2185,9 +2152,7 @@ class Client(object): "state": "active" if self._markAlive else "offline", } return self._get( - "https://{}-edge-chat.facebook.com/pull".format(self._pull_channel), - data, - as_json=True, + "https://{}-edge-chat.facebook.com/pull".format(self._pull_channel), data ) def _parseDelta(self, m): From 5b57d49a3e7b397fede38e2884a70b0ffe47014c Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 2 Jul 2019 15:14:02 +0200 Subject: [PATCH 6/9] Remove Client._postFile --- fbchat/_client.py | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/fbchat/_client.py b/fbchat/_client.py index ef0e4e0..94b9ca6 100644 --- a/fbchat/_client.py +++ b/fbchat/_client.py @@ -128,9 +128,9 @@ class Client(object): return self._get(url, query=query, error_retries=error_retries - 1) raise e - def _post(self, url, query=None, as_graphql=False, error_retries=3): + def _post(self, url, query=None, files=None, as_graphql=False, error_retries=3): payload = self._generatePayload(query) - r = self._state._session.post(prefix_url(url), data=payload) + r = self._state._session.post(prefix_url(url), data=payload, files=files) try: if as_graphql: content = check_request(r, as_json=False) @@ -142,23 +142,12 @@ class Client(object): return self._post( url, query=query, + files=files, as_graphql=as_graphql, error_retries=error_retries - 1, ) raise e - def _postFile(self, url, files=None, query=None, error_retries=3): - payload = self._generatePayload(query) - r = self._state._session.post(prefix_url(url), data=payload, files=files) - try: - return check_request(r) - except FBchatFacebookError as e: - if error_retries > 0 and self._fix_fb_errors(e.fb_error_code): - return self._postFile( - url, files=files, query=query, error_retries=error_retries - 1 - ) - raise e - def graphql_requests(self, *queries): """ :param queries: Zero or more GraphQL objects @@ -1268,10 +1257,8 @@ class Client(object): data = {"voice_clip": voice_clip} - j = self._postFile( - "https://upload.facebook.com/ajax/mercury/upload.php", - files=file_dict, - query=data, + j = self._post( + "https://upload.facebook.com/ajax/mercury/upload.php", data, files=file_dict ) if len(j["payload"]["metadata"]) != len(files): From c3a974a495cc1ae0ebb4a7d90cb08f8e6bb86221 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 2 Jul 2019 15:34:23 +0200 Subject: [PATCH 7/9] Refactor _util.check_request --- fbchat/_util.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/fbchat/_util.py b/fbchat/_util.py index b672082..823e313 100644 --- a/fbchat/_util.py +++ b/fbchat/_util.py @@ -140,25 +140,31 @@ def check_json(j): def check_request(r, as_json=True): - if not r.ok: + check_http_code(r.status_code) + content = get_decoded_r(r) + check_content(content) + return to_json(content) if as_json else content + + +def check_http_code(code): + if 400 <= code < 600: raise FBchatFacebookError( - "Error when sending request: Got {} response".format(r.status_code), - request_status_code=r.status_code, + "Error when sending request: Got {} response".format(code), + request_status_code=code, ) - content = get_decoded_r(r) +def check_content(content, as_json=True): if content is None or len(content) == 0: raise FBchatFacebookError("Error when sending request: Got empty response") - if as_json: - content = strip_json_cruft(content) - j = parse_json(content) - check_json(j) - log.debug(j) - return j - else: - return content + +def to_json(content): + content = strip_json_cruft(content) + j = parse_json(content) + check_json(j) + log.debug(j) + return j def get_jsmods_require(j, index): From 7f84ca8d0c13d654ea56304d44e418b80d983014 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 2 Jul 2019 15:50:58 +0200 Subject: [PATCH 8/9] Add Client._payload_post helper --- fbchat/_client.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fbchat/_client.py b/fbchat/_client.py index 94b9ca6..f075760 100644 --- a/fbchat/_client.py +++ b/fbchat/_client.py @@ -148,6 +148,13 @@ class Client(object): ) raise e + def _payload_post(self, url, data, files=None): + j = self._post(url, data, files=files) + try: + return j["payload"] + except (KeyError, TypeError): + raise FBchatException("Missing payload: {}".format(j)) + def graphql_requests(self, *queries): """ :param queries: Zero or more GraphQL objects From 4a898b3ff546ced51ba635dbe19370cb22ed2ccf Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Tue, 2 Jul 2019 15:52:02 +0200 Subject: [PATCH 9/9] Use Client._payload_post helper where relevant --- fbchat/_client.py | 124 ++++++++++++++++++++++++---------------------- 1 file changed, 64 insertions(+), 60 deletions(-) diff --git a/fbchat/_client.py b/fbchat/_client.py index f075760..bfee672 100644 --- a/fbchat/_client.py +++ b/fbchat/_client.py @@ -413,12 +413,10 @@ class Client(object): :raises: FBchatException if request failed """ data = {"viewer": self._uid} - j = self._post("/chat/user_info_all", query=data) - if j.get("payload") is None: - raise FBchatException("Missing payload while fetching users: {}".format(j)) + j = self._payload_post("/chat/user_info_all", data) users = [] - for data in j["payload"].values(): + for data in j.values(): if data["type"] in ["user", "friend"]: if data["id"] in ["0", 0]: # Skip invalid users @@ -525,9 +523,9 @@ class Client(object): "identifier": "thread_fbid", "thread_fbid": thread_id, } - j = self._post("/ajax/mercury/search_snippets.php?dpr=1", data) + j = self._payload_post("/ajax/mercury/search_snippets.php?dpr=1", data) - result = j["payload"]["search_snippets"][query] + result = j["search_snippets"][query] snippets = result[thread_id]["snippets"] if result.get(thread_id) else [] for snippet in snippets: yield snippet["message_id"] @@ -570,8 +568,8 @@ class Client(object): :raises: FBchatException if request failed """ data = {"query": query, "snippetLimit": thread_limit} - j = self._post("/ajax/mercury/search_snippets.php?dpr=1", data) - result = j["payload"]["search_snippets"][query] + j = self._payload_post("/ajax/mercury/search_snippets.php?dpr=1", data) + result = j["search_snippets"][query] if not result: return {} @@ -588,14 +586,14 @@ class Client(object): def _fetchInfo(self, *ids): data = {"ids[{}]".format(i): _id for i, _id in enumerate(ids)} - j = self._post("/chat/user_info/", data) + j = self._payload_post("/chat/user_info/", data) - if j.get("payload") is None or j["payload"].get("profiles") is None: + if j.get("profiles") is None: raise FBchatException("No users/pages returned: {}".format(j)) entries = {} - for _id in j["payload"]["profiles"]: - k = j["payload"]["profiles"][_id] + for _id in j["profiles"]: + k = j["profiles"][_id] if k["type"] in ["user", "friend"]: entries[_id] = { "id": _id, @@ -858,10 +856,10 @@ class Client(object): "last_action_timestamp": now() - 60 * 1000 # 'last_action_timestamp': 0 } - j = self._post("/ajax/mercury/unread_threads.php", form) + j = self._payload_post("/ajax/mercury/unread_threads.php", form) - payload = j["payload"]["unread_thread_fbids"][0] - return payload["thread_fbids"] + payload["other_user_fbids"] + result = j["unread_thread_fbids"][0] + return result["thread_fbids"] + result["other_user_fbids"] def fetchUnseen(self): """ @@ -871,10 +869,10 @@ class Client(object): :rtype: list :raises: FBchatException if request failed """ - j = self._post("/mercury/unseen_thread_ids/", None) + j = self._payload_post("/mercury/unseen_thread_ids/", None) - payload = j["payload"]["unseen_thread_fbids"][0] - return payload["thread_fbids"] + payload["other_user_fbids"] + result = j["unseen_thread_fbids"][0] + return result["thread_fbids"] + result["other_user_fbids"] def fetchImageUrl(self, image_id): """Fetches the url to the original image from an image attachment ID @@ -917,8 +915,8 @@ class Client(object): :raises: FBchatException if request failed """ data = {"question_id": poll_id} - j = self._post("/ajax/mercury/get_poll_options", data) - return [PollOption._from_graphql(m) for m in j["payload"]] + j = self._payload_post("/ajax/mercury/get_poll_options", data) + return [PollOption._from_graphql(m) for m in j] def fetchPlanInfo(self, plan_id): """ @@ -930,8 +928,8 @@ class Client(object): :raises: FBchatException if request failed """ data = {"event_reminder_id": plan_id} - j = self._post("/ajax/eventreminder", data) - return Plan._from_fetch(j["payload"]) + j = self._payload_post("/ajax/eventreminder", data) + return Plan._from_fetch(j) def _getPrivateData(self): j = self.graphql_request(GraphQL(doc_id="1868889766468115")) @@ -1190,7 +1188,7 @@ class Client(object): :param mid: :ref:`Message ID ` of the message to unsend """ data = {"message_id": mid} - j = self._post("/messaging/unsend_message/?dpr=1", data) + j = self._payload_post("/messaging/unsend_message/?dpr=1", data) def _sendLocation( self, location, current=True, message=None, thread_id=None, thread_type=None @@ -1264,18 +1262,18 @@ class Client(object): data = {"voice_clip": voice_clip} - j = self._post( + j = self._payload_post( "https://upload.facebook.com/ajax/mercury/upload.php", data, files=file_dict ) - if len(j["payload"]["metadata"]) != len(files): + if len(j["metadata"]) != len(files): raise FBchatException( "Some files could not be uploaded: {}, {}".format(j, files) ) return [ (data[mimetype_to_key(data["filetype"])], data["filetype"]) - for data in j["payload"]["metadata"] + for data in j["metadata"] ] def _sendFiles( @@ -1444,7 +1442,7 @@ class Client(object): "attachment_id": attachment_id, "recipient_map[{}]".format(generateOfflineThreadingID()): thread_id, } - j = self._post("/mercury/attachments/forward/", data) + j = self._payload_post("/mercury/attachments/forward/", data) def createGroup(self, message, user_ids): """ @@ -1510,7 +1508,7 @@ class Client(object): thread_id, thread_type = self._getThread(thread_id, None) data = {"uid": user_id, "tid": thread_id} - j = self._post("/chat/remove_participants/", data) + j = self._payload_post("/chat/remove_participants/", data) def _adminStatus(self, admin_ids, admin, thread_id=None): thread_id, thread_type = self._getThread(thread_id, None) @@ -1522,7 +1520,7 @@ class Client(object): for i, admin_id in enumerate(admin_ids): data["admin_ids[{}]".format(i)] = str(admin_id) - j = self._post("/messaging/save_admins/?dpr=1", data) + j = self._payload_post("/messaging/save_admins/?dpr=1", data) def addGroupAdmins(self, admin_ids, thread_id=None): """ @@ -1555,7 +1553,7 @@ class Client(object): thread_id, thread_type = self._getThread(thread_id, None) data = {"set_mode": int(require_admin_approval), "thread_fbid": thread_id} - j = self._post("/messaging/set_approval_mode/?dpr=1", data) + j = self._payload_post("/messaging/set_approval_mode/?dpr=1", data) def _usersApproval(self, user_ids, approve, thread_id=None): thread_id, thread_type = self._getThread(thread_id, None) @@ -1606,7 +1604,7 @@ class Client(object): data = {"thread_image_id": image_id, "thread_id": thread_id} - j = self._post("/messaging/set_thread_image/?dpr=1", data) + j = self._payload_post("/messaging/set_thread_image/?dpr=1", data) return image_id def changeGroupImageRemote(self, image_url, thread_id=None): @@ -1653,7 +1651,7 @@ class Client(object): ) data = {"thread_name": title, "thread_id": thread_id} - j = self._post("/messaging/set_thread_name/?dpr=1", data) + j = self._payload_post("/messaging/set_thread_name/?dpr=1", data) def changeNickname( self, nickname, user_id, thread_id=None, thread_type=ThreadType.USER @@ -1675,7 +1673,7 @@ class Client(object): "participant_id": user_id, "thread_or_other_fbid": thread_id, } - j = self._post( + j = self._payload_post( "/messaging/save_thread_nickname/?source=thread_settings&dpr=1", data ) @@ -1694,7 +1692,7 @@ class Client(object): "color_choice": color.value if color != ThreadColor.MESSENGER_BLUE else "", "thread_or_other_fbid": thread_id, } - j = self._post( + j = self._payload_post( "/messaging/save_thread_color/?source=thread_settings&dpr=1", data ) @@ -1711,7 +1709,7 @@ class Client(object): thread_id, thread_type = self._getThread(thread_id, None) data = {"emoji_choice": emoji, "thread_or_other_fbid": thread_id} - j = self._post( + j = self._payload_post( "/messaging/save_thread_emoji/?source=thread_settings&dpr=1", data ) @@ -1732,7 +1730,7 @@ class Client(object): "reaction": reaction.value if reaction else None, } data = {"doc_id": 1491398900900362, "variables": json.dumps({"data": data})} - j = self._post("/webgraphql/mutation", data) + j = self._payload_post("/webgraphql/mutation", data) def createPlan(self, plan, thread_id=None): """ @@ -1754,7 +1752,7 @@ class Client(object): "location_name": plan.location or "", "acontext": ACONTEXT, } - j = self._post("/ajax/eventreminder/create", data) + j = self._payload_post("/ajax/eventreminder/create", data) def editPlan(self, plan, new_plan): """ @@ -1774,7 +1772,7 @@ class Client(object): "title": new_plan.title, "acontext": ACONTEXT, } - j = self._post("/ajax/eventreminder/submit", data) + j = self._payload_post("/ajax/eventreminder/submit", data) def deletePlan(self, plan): """ @@ -1784,7 +1782,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"event_reminder_id": plan.uid, "delete": "true", "acontext": ACONTEXT} - j = self._post("/ajax/eventreminder/submit", data) + j = self._payload_post("/ajax/eventreminder/submit", data) def changePlanParticipation(self, plan, take_part=True): """ @@ -1799,7 +1797,7 @@ class Client(object): "guest_state": "GOING" if take_part else "DECLINED", "acontext": ACONTEXT, } - j = self._post("/ajax/eventreminder/rsvp", data) + j = self._payload_post("/ajax/eventreminder/rsvp", data) def eventReminder(self, thread_id, time, title, location="", location_id=""): """ @@ -1829,7 +1827,7 @@ class Client(object): data["option_text_array[{}]".format(i)] = option.text data["option_is_selected_array[{}]".format(i)] = str(int(option.vote)) - j = self._post("/messaging/group_polling/create_poll/?dpr=1", data) + j = self._payload_post("/messaging/group_polling/create_poll/?dpr=1", data) def updatePollVote(self, poll_id, option_ids=[], new_options=[]): """ @@ -1851,7 +1849,7 @@ class Client(object): for i, option_text in enumerate(new_options): data["new_options[{}]".format(i)] = option_text - j = self._post("/messaging/group_polling/update_vote/?dpr=1", data) + j = self._payload_post("/messaging/group_polling/update_vote/?dpr=1", data) def setTypingStatus(self, status, thread_id=None, thread_type=None): """ @@ -1872,7 +1870,7 @@ class Client(object): "to": thread_id if thread_type == ThreadType.USER else "", "source": "mercury-chat", } - j = self._post("/ajax/messaging/typ.php", data) + j = self._payload_post("/ajax/messaging/typ.php", data) """ END SEND METHODS @@ -1892,7 +1890,7 @@ class Client(object): "thread_ids[%s][0]" % thread_id: message_id, } - j = self._post("/ajax/mercury/delivery_receipts.php", data) + j = self._payload_post("/ajax/mercury/delivery_receipts.php", data) return True def _readStatus(self, read, thread_ids): @@ -1903,7 +1901,7 @@ class Client(object): for thread_id in thread_ids: data["ids[{}]".format(thread_id)] = "true" if read else "false" - j = self._post("/ajax/mercury/change_read_status.php", data) + j = self._payload_post("/ajax/mercury/change_read_status.php", data) def markAsRead(self, thread_ids=None): """ @@ -1930,7 +1928,7 @@ class Client(object): .. todo:: Documenting this """ - j = self._post("/ajax/mercury/mark_seen.php", {"seen_timestamp": now()}) + j = self._payload_post("/ajax/mercury/mark_seen.php", {"seen_timestamp": now()}) def friendConnect(self, friend_id): """ @@ -1939,7 +1937,7 @@ class Client(object): """ data = {"to_friend": friend_id, "action": "confirm"} - j = self._post("/ajax/add_friend/action.php?dpr=1", data) + j = self._payload_post("/ajax/add_friend/action.php?dpr=1", data) def removeFriend(self, friend_id=None): """ @@ -1950,7 +1948,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"uid": friend_id} - j = self._post("/ajax/profile/removefriendconfirm.php", data) + j = self._payload_post("/ajax/profile/removefriendconfirm.php", data) return True def blockUser(self, user_id): @@ -1962,7 +1960,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"fbid": user_id} - j = self._post("/messaging/block_messages/?dpr=1", data) + j = self._payload_post("/messaging/block_messages/?dpr=1", data) return True def unblockUser(self, user_id): @@ -1974,7 +1972,7 @@ class Client(object): :raises: FBchatException if request failed """ data = {"fbid": user_id} - j = self._post("/messaging/unblock_messages/?dpr=1", data) + j = self._payload_post("/messaging/unblock_messages/?dpr=1", data) return True def moveThreads(self, location, thread_ids): @@ -1997,17 +1995,17 @@ class Client(object): for thread_id in thread_ids: data_archive["ids[{}]".format(thread_id)] = "true" data_unpin["ids[{}]".format(thread_id)] = "false" - j_archive = self._post( + j_archive = self._payload_post( "/ajax/mercury/change_archived_status.php?dpr=1", data_archive ) - j_unpin = self._post( + j_unpin = self._payload_post( "/ajax/mercury/change_pinned_status.php?dpr=1", data_unpin ) else: data = dict() for i, thread_id in enumerate(thread_ids): data["{}[{}]".format(location.name.lower(), i)] = thread_id - j = self._post("/ajax/mercury/move_thread.php", data) + j = self._payload_post("/ajax/mercury/move_thread.php", data) return True def deleteThreads(self, thread_ids): @@ -2025,8 +2023,12 @@ class Client(object): for i, thread_id in enumerate(thread_ids): data_unpin["ids[{}]".format(thread_id)] = "false" data_delete["ids[{}]".format(i)] = thread_id - j_unpin = self._post("/ajax/mercury/change_pinned_status.php?dpr=1", data_unpin) - j_delete = self._post("/ajax/mercury/delete_thread.php?dpr=1", data_delete) + j_unpin = self._payload_post( + "/ajax/mercury/change_pinned_status.php?dpr=1", data_unpin + ) + j_delete = self._payload_post( + "/ajax/mercury/delete_thread.php?dpr=1", data_delete + ) return True def markAsSpam(self, thread_id=None): @@ -2038,7 +2040,7 @@ class Client(object): :raises: FBchatException if request failed """ thread_id, thread_type = self._getThread(thread_id, None) - j = self._post("/ajax/mercury/mark_spam.php?dpr=1", {"id": thread_id}) + j = self._payload_post("/ajax/mercury/mark_spam.php?dpr=1", {"id": thread_id}) return True def deleteMessages(self, message_ids): @@ -2053,7 +2055,7 @@ class Client(object): data = dict() for i, message_id in enumerate(message_ids): data["message_ids[{}]".format(i)] = message_id - j = self._post("/ajax/mercury/delete_messages.php?dpr=1", data) + j = self._payload_post("/ajax/mercury/delete_messages.php?dpr=1", data) return True def muteThread(self, mute_time=-1, thread_id=None): @@ -2065,7 +2067,7 @@ class Client(object): """ thread_id, thread_type = self._getThread(thread_id, None) data = {"mute_settings": str(mute_time), "thread_fbid": thread_id} - j = self._post("/ajax/mercury/change_mute_thread.php?dpr=1", data) + j = self._payload_post("/ajax/mercury/change_mute_thread.php?dpr=1", data) def unmuteThread(self, thread_id=None): """ @@ -2084,7 +2086,9 @@ class Client(object): """ thread_id, thread_type = self._getThread(thread_id, None) data = {"reactions_mute_mode": int(mute), "thread_fbid": thread_id} - j = self._post("/ajax/mercury/change_reactions_mute_thread/?dpr=1", data) + j = self._payload_post( + "/ajax/mercury/change_reactions_mute_thread/?dpr=1", data + ) def unmuteThreadReactions(self, thread_id=None): """ @@ -2103,7 +2107,7 @@ class Client(object): """ thread_id, thread_type = self._getThread(thread_id, None) data = {"mentions_mute_mode": int(mute), "thread_fbid": thread_id} - j = self._post("/ajax/mercury/change_mentions_mute_thread/?dpr=1", data) + j = self._payload_post("/ajax/mercury/change_mentions_mute_thread/?dpr=1", data) def unmuteThreadMentions(self, thread_id=None): """