diff --git a/examples/interract.py b/examples/interract.py index 498e9bb..ca9600a 100644 --- a/examples/interract.py +++ b/examples/interract.py @@ -29,12 +29,12 @@ thread.send_text( # Will send the image located at `` with open("", "rb") as f: - files = session._upload([("image_name.png", f, "image/png")]) + files = client.upload([("image_name.png", f, "image/png")]) thread.send_text(text="This is a local image", files=files) # Will download the image at the URL ``, and then send it r = requests.get("") -files = session._upload([("image_name.png", r.content, "image/png")]) +files = client.upload([("image_name.png", r.content, "image/png")]) thread.send_files(files) # Alternative to .send_text diff --git a/fbchat/_client.py b/fbchat/_client.py index a2672cb..dacb0f0 100644 --- a/fbchat/_client.py +++ b/fbchat/_client.py @@ -4,7 +4,7 @@ import datetime from ._common import log, attrs_default from . import _exception, _util, _graphql, _session, _threads, _models -from typing import Sequence, Iterable, Tuple, Optional, Set +from typing import Sequence, Iterable, Tuple, Optional, Set, BinaryIO @attrs_default @@ -500,6 +500,41 @@ class Client: data = self._get_private_data() return [j["display_email"] for j in data["all_emails"]] + def upload( + self, files: Iterable[Tuple[str, BinaryIO, str]], voice_clip: bool = False + ) -> Sequence[Tuple[str, str]]: + """Upload files to Facebook. + + `files` should be a list of files that requests can upload, see + `requests.request `_. + + Example: + >>> with open("file.txt", "rb") as f: + ... (file,) = client.upload([("file.txt", f, "text/plain")]) + ... + >>> file + ("1234", "text/plain") + Return: + Tuples with a file's ID and mimetype. + This result can be passed straight on to `ThreadABC.send_files`, or used in + `Group.set_image`. + """ + file_dict = {"upload_{}".format(i): f for i, f in enumerate(files)} + + data = {"voice_clip": voice_clip} + + j = self.session._payload_post( + "https://upload.facebook.com/ajax/mercury/upload.php", data, files=file_dict + ) + + if len(j["metadata"]) != len(file_dict): + raise _exception.ParseError("Some files could not be uploaded", data=j) + + return [ + (str(item[_util.mimetype_to_key(item["filetype"])]), item["filetype"]) + for item in j["metadata"] + ] + def mark_as_delivered(self, message: _models.Message): """Mark a message as delivered. diff --git a/fbchat/_session.py b/fbchat/_session.py index 03f062e..26df79f 100644 --- a/fbchat/_session.py +++ b/fbchat/_session.py @@ -9,7 +9,7 @@ import urllib.parse from ._common import log, kw_only from . import _graphql, _util, _exception -from typing import Optional, Tuple, Mapping, BinaryIO, Sequence, Iterable, Callable +from typing import Optional, Tuple, Mapping, Callable FB_DTSG_REGEX = re.compile(r'name="fb_dtsg" value="(.*?)"') @@ -31,7 +31,9 @@ def base36encode(number: int) -> str: def prefix_url(url: str) -> str: - return "https://www.facebook.com" + url + if url.startswith("/"): + return "https://www.facebook.com" + url + return url def generate_message_id(now: datetime.datetime, client_id: str) -> str: @@ -397,39 +399,6 @@ class Session: } return self._post("/api/graphqlbatch/", data, as_graphql=True) - def _upload( - self, files: Iterable[Tuple[str, BinaryIO, str]], voice_clip: bool = False - ) -> Sequence[Tuple[str, str]]: - """Upload files to Facebook. - - `files` should be a list of files that requests can upload, see - `requests.request `_. - - Example: - >>> with open("file.txt", "rb") as f: - ... (file,) = session._upload([("file.txt", f, "text/plain")]) - ... - >>> file - ("1234", "text/plain") - Return: - Tuples with a file's ID and mimetype. - """ - file_dict = {"upload_{}".format(i): f for i, f in enumerate(files)} - - data = {"voice_clip": voice_clip} - - j = self._payload_post( - "https://upload.facebook.com/ajax/mercury/upload.php", data, files=file_dict - ) - - if len(j["metadata"]) != len(file_dict): - raise _exception.ParseError("Some files could not be uploaded", data=j) - - return [ - (data[_util.mimetype_to_key(data["filetype"])], data["filetype"]) - for data in j["metadata"] - ] - def _do_send_request(self, data): now = datetime.datetime.utcnow() offline_threading_id = _util.generate_offline_threading_id() diff --git a/fbchat/_threads/_abc.py b/fbchat/_threads/_abc.py index ebaa831..ed9159a 100644 --- a/fbchat/_threads/_abc.py +++ b/fbchat/_threads/_abc.py @@ -111,7 +111,8 @@ class ThreadABC(metaclass=abc.ABCMeta): Args: text: Text to send mentions: Optional mentions - files: Optional tuples, each containing an uploaded file's ID and mimetype + files: Optional tuples, each containing an uploaded file's ID and mimetype. + See `ThreadABC.send_files` for an example. reply_to_id: Optional message to reply to Example: @@ -223,9 +224,9 @@ class ThreadABC(metaclass=abc.ABCMeta): Upload and send a video to a thread. >>> with open("video.mp4", "rb") as f: - ... files = session._upload([("video.mp4", f, "video/mp4")]) + ... files = client.upload([("video.mp4", f, "video/mp4")]) >>> - >>> thread.send_files(files=files) + >>> thread.send_files(files) """ return self.send_text(text=None, files=files) diff --git a/fbchat/_threads/_group.py b/fbchat/_threads/_group.py index 7cfd35b..d80c5fb 100644 --- a/fbchat/_threads/_group.py +++ b/fbchat/_threads/_group.py @@ -116,7 +116,7 @@ class Group(ThreadABC): Upload an image, and use it as the group image. >>> with open("image.png", "rb") as f: - ... (file,) = session._upload([("image.png", f, "image/png")]) + ... (file,) = client.upload([("image.png", f, "image/png")]) ... >>> group.set_image(file[0]) """