diff --git a/fbchat/_client.py b/fbchat/_client.py index fc856058..b63dc19c 100644 --- a/fbchat/_client.py +++ b/fbchat/_client.py @@ -1066,6 +1066,52 @@ def sendMessage(self, message, thread_id=None, thread_type=ThreadType.USER): Message(text=message), thread_id=thread_id, thread_type=thread_type ) + def sendUri(self, uri, message=None, thread_id=None, thread_type=ThreadType.USER): + """Send a uri preview to a thread. + + Args: + uri: uri to preview + message (Message): Message to send + thread_id: User/Group ID to send to. See :ref:`intro_threads` + thread_type (ThreadType): See :ref:`intro_threads` + + Returns: + :ref:`Message ID ` of the sent message + + Raises: + FBchatException: If request failed + """ + + urlData = self._state._uri_share_data({"uri": uri}) + thread_id, thread_type = self._getThread(thread_id, thread_type) + thread = thread_type._to_class()(thread_id) + data = thread._to_send_data() + if message is not None: + data.update(message._to_send_data()) + data["action_type"] = "ma-type:user-generated-message" + data["shareable_attachment[share_type]"] = urlData["share_type"] + # most uri params will come back as dict + if isinstance(urlData["share_params"], dict): + data["has_attachment"] = True + for key in urlData["share_params"]: + if isinstance(urlData["share_params"][key], dict): + for key2 in urlData["share_params"][key]: + data[ + "shareable_attachment[share_params][{}][{}]".format( + key, key2 + ) + ] = urlData["share_params"][key][key2] + else: + data[ + "shareable_attachment[share_params][{}]".format(key) + ] = urlData["share_params"][key] + # some (such as facebook profile pages) will just be a list + else: + data["has_attachment"] = False + for index, val in enumerate(urlData["share_params"]): + data["shareable_attachment[share_params][{}]".format(index)] = val + return self._doSendRequest(data) + def sendEmoji( self, emoji=None, diff --git a/fbchat/_state.py b/fbchat/_state.py index 3aebcecf..3e705c89 100644 --- a/fbchat/_state.py +++ b/fbchat/_state.py @@ -9,7 +9,7 @@ from . import _graphql, _util, _exception -FB_DTSG_REGEX = re.compile(r'name="fb_dtsg" value="(.*?)"') +FB_DTSG_REGEX = re.compile(r'"token":"(.*?)"') def get_user_id(session): @@ -138,13 +138,21 @@ def login(cls, email, password, on_2fa_callback, user_agent=None): r = session.post("https://m.facebook.com/login.php?login_attempt=1", data=data) + if "cookie" in r.url: + beg = r.text.find("action=") + 8 + end = r.text.find("\"", beg) + urlCookies = "https://m.facebook.com" + r.text[beg:end] + payload = {i["name"]: i["value"] for i in find_input_fields(r.text).find_all('input')} + r = session.post(urlCookies, data=payload) + # Usually, 'Checkpoint' will refer to 2FA if "checkpoint" in r.url and ('id="approvals_code"' in r.text.lower()): code = on_2fa_callback() r = _2fa_helper(session, code, r) # Sometimes Facebook tries to show the user a "Save Device" dialog - if "save-device" in r.url: + # was getting a different url for 'same device' page; not sure if this applies to everyone + if "checkpoint" in r.url: r = session.get("https://m.facebook.com/login/save-device/cancel/") if is_home(r.url): @@ -187,7 +195,7 @@ def from_session(cls, session): # Fall back to searching with a regex fb_dtsg = FB_DTSG_REGEX.search(r.text).group(1) - revision = int(r.text.split('"client_revision":', 1)[1].split(",", 1)[0]) + revision = 1 logout_h_element = soup.find("input", {"name": "h"}) logout_h = logout_h_element["value"] if logout_h_element else None @@ -331,3 +339,10 @@ def _do_send_request(self, data): "Error when sending message: " "No message IDs could be found: {}".format(j) ) + + def _uri_share_data(self, data): + data["image_height"] = 960 + data["image_width"] = 960 + data["__user"] = self.user_id + j = self._post("/message_share_attachment/fromURI/", data) + return j["payload"]["share_data"]