From c8838fb72b9855be630039365d1dbe624634f38c Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Thu, 26 Oct 2023 23:41:12 -0400 Subject: [PATCH 01/43] Refactored setup.py --- setup.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index c33d54b..2e61bab 100644 --- a/setup.py +++ b/setup.py @@ -2,25 +2,31 @@ VERSION = "0.1.1" DESCRIPTION = "A Python API wrapper for the social media app Lapse." -LONG_DESCRIPTION = "An unofficial API wrapper for the social media app Lapse." + +with open("README.md", 'r') as f: + LONG_DESCRIPTION = f.read() with open("requirements.txt", 'r', encoding="utf-16") as f: requirements = [i.strip() for i in f.readlines()] +with open("LICENSE", 'r') as f: + LICENSE = f.read() + setup(name='lapsepy', version=VERSION, description=DESCRIPTION, long_description_content_type="text/markdown", long_description=LONG_DESCRIPTION, + license=LICENSE, author="Quintin Dunn", author_email="dunnquintin07@gmail.com", url="https://github.com/quintindunn/lapsepy", packages=find_packages(), keywords=['social media', 'lapsepy', 'api', 'api wrapper'], classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Operating System :: Microsoft :: Windows :: Windows 10', - 'Programming Language :: Python :: 3', + 'Development Status :: 5 - Production/Stable', + 'Operating System :: Microsoft :: Windows :: Windows 10', + 'Programming Language :: Python :: 3', ], install_requires=requirements ) From 7905bb6c08e08de02bd9b82e5e7f64ce1a6d105c Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 19:07:01 -0400 Subject: [PATCH 02/43] Added SendKudosGQL --- lapsepy/journal/factory/friends_factory.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lapsepy/journal/factory/friends_factory.py b/lapsepy/journal/factory/friends_factory.py index 6120cff..95d14c5 100644 --- a/lapsepy/journal/factory/friends_factory.py +++ b/lapsepy/journal/factory/friends_factory.py @@ -156,3 +156,18 @@ def _render_variables(self): "mutualLimit": self.mutual_limit, "popularLimit": self.popular_limit } + + +class SendKudosGQL(BaseGQL): + def __init__(self, user_id: str): + super().__init__("SendKudosGraphQLMutation", "mutation SendKudosGraphQLMutation($input: SendKudosInput!) " + "{ sendKudos(input: $input) { __typename success } }") + + self.user_id = user_id + + self.variables = {} + + def _render_variables(self): + self.variables['input'] = { + "id": self.user_id + } From 2f5aca4c532a2cf2a4aff836a0dd0950bdc7628e Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 19:08:02 -0400 Subject: [PATCH 03/43] Added journal.send_kudos --- lapsepy/journal/journal.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lapsepy/journal/journal.py b/lapsepy/journal/journal.py index f9668d7..0b497f4 100644 --- a/lapsepy/journal/journal.py +++ b/lapsepy/journal/journal.py @@ -13,7 +13,7 @@ import requests -from .factory.friends_factory import FriendsFeedItemsGQL, ProfileDetailsGQL +from .factory.friends_factory import FriendsFeedItemsGQL, ProfileDetailsGQL, SendKudosGQL from .factory.media_factory import ImageUploadURLGQL, CreateMediaGQL, SendInstantsGQL from lapsepy.journal.factory.profile_factory import SaveBioGQL, SaveDisplayNameGQL, SaveUsernameGQL, SaveEmojisGQL, \ SaveDOBGQL @@ -178,6 +178,15 @@ def upload_instant(self, im: Image.Image, user_id: str, file_uuid: str | None = time_limit=time_limit).to_dict() self._sync_journal_call(query) + def send_kudos(self, user_id: str): + """ + Sends kudos (vibes) to a given user + :param user_id: id of the user to send kudos to. + :return: + """ + query = SendKudosGQL(user_id=user_id).to_dict() + return self._sync_journal_call(query) + def get_friends_feed(self, count: int = 10) -> list[Profile]: """ Gets your friend upload feed. From 96717dee7d4299b88da41b2ce7f6c57f9d498e99 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 19:09:08 -0400 Subject: [PATCH 04/43] Added lapse.send_kudos --- lapsepy/lapse/lapse.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lapsepy/lapse/lapse.py b/lapsepy/lapse/lapse.py index c48fba1..8f2cf61 100644 --- a/lapsepy/lapse/lapse.py +++ b/lapsepy/lapse/lapse.py @@ -86,6 +86,17 @@ def upload_instant(self, im: Image, user: str | Profile, file_uuid: str | None = return self.journal.upload_instant(im=im, user_id=user, file_uuid=file_uuid, im_id=im_id, caption=caption, time_limit=time_limit) + def send_kudos(self, user: str | Profile): + """ + Sends kudos (vibes) to a user. + :param user: ID / Object of user to send it to. + :return: + """ + if isinstance(user, Profile): + user = user.user_id + + self.journal.send_kudos(user) + def get_friends_feed(self, count: int = 10): """ Gets your friend upload feed. From 4ef4a9a01b83575cdd620c622939a26c9c4e1503 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 19:13:58 -0400 Subject: [PATCH 05/43] Added examples/send_kudos_1.py --- examples/send_kudos/send_kudos_1.py | 14 ++++++++++++++ lapsepy/journal/journal.py | 7 ++++++- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 examples/send_kudos/send_kudos_1.py diff --git a/examples/send_kudos/send_kudos_1.py b/examples/send_kudos/send_kudos_1.py new file mode 100644 index 0000000..578320d --- /dev/null +++ b/examples/send_kudos/send_kudos_1.py @@ -0,0 +1,14 @@ +import os +from PIL import Image +from lapsepy.lapse import Lapse + +if __name__ == '__main__': + lapse = Lapse(refresh_token=os.getenv("REFRESH_TOKEN")) + + # Develop in 15 seconds + friend_id = input("Friend UUID: ") + + # Get friend object + friend = lapse.get_profile_by_id(friend_id) + + lapse.send_kudos(user=friend) diff --git a/lapsepy/journal/journal.py b/lapsepy/journal/journal.py index 0b497f4..4c9db44 100644 --- a/lapsepy/journal/journal.py +++ b/lapsepy/journal/journal.py @@ -185,7 +185,12 @@ def send_kudos(self, user_id: str): :return: """ query = SendKudosGQL(user_id=user_id).to_dict() - return self._sync_journal_call(query) + response = self._sync_journal_call(query) + + if not response.get("data", {}).get("sendKudos", {}).get("success"): + raise SyncJournalException("Error sending kudos, could you already have reached your daily limit?") + + def get_friends_feed(self, count: int = 10) -> list[Profile]: """ From 0c9d761bdd44f9fd55af78181eaea9c2137e2885 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 19:14:32 -0400 Subject: [PATCH 06/43] Added render call to SendKudosGQL --- lapsepy/journal/factory/friends_factory.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lapsepy/journal/factory/friends_factory.py b/lapsepy/journal/factory/friends_factory.py index 95d14c5..98af07c 100644 --- a/lapsepy/journal/factory/friends_factory.py +++ b/lapsepy/journal/factory/friends_factory.py @@ -167,6 +167,8 @@ def __init__(self, user_id: str): self.variables = {} + self._render_variables() + def _render_variables(self): self.variables['input'] = { "id": self.user_id From 3156ddf06263db89df5257dfee9ef1c195b995ef Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 19:30:30 -0400 Subject: [PATCH 07/43] Moved Profile structure to structures/profile.py --- lapsepy/journal/structures.py | 84 ------------------------ lapsepy/journal/structures/profile.py | 92 +++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 84 deletions(-) create mode 100644 lapsepy/journal/structures/profile.py diff --git a/lapsepy/journal/structures.py b/lapsepy/journal/structures.py index 3868726..103c22c 100644 --- a/lapsepy/journal/structures.py +++ b/lapsepy/journal/structures.py @@ -13,90 +13,6 @@ logger = logging.getLogger("lapsepy.journal.structures.py") -def _dt_from_iso(dt_str: str): - return datetime.fromisoformat(dt_str) - - -class Profile: - def __init__(self, user_id: str, username: str, display_name: str, profile_photo_name: str, bio: str | None, - emojis: list[str], is_friends: bool, blocked_me: bool, kudos: int, tags: list[dict], - is_blocked: bool = False, friends: list["Profile"] = None): - if friends is None: - friends = [] - - self.bio: str = bio - self.blocked_me: bool = blocked_me - self.user_display_name: str = display_name - self.emojis: list[str] = emojis - self.is_friends: bool = is_friends - self.kudos = kudos - self.profile_photo_name: str = profile_photo_name - self.tags = tags - self.user_id: str = user_id - self.username: str = username - self.media: list[Snap] = [] - self.is_blocked = is_blocked - - self.friends: list["Profile"] = friends - - self.profile_picture: Image.Image | None = None - - @staticmethod - def from_dict(profile_data: dict) -> "Profile": - """ - Generates a Profile object from a dictionary with the necessary profile data - :param profile_data: Dictionary containing the necessary data. - :return: Profile object prefilled with the data. - """ - logger.debug("Creating new Profile object from dictionary.") - - pd = profile_data - return Profile( - bio=pd.get('bio'), - blocked_me=pd.get('blockedMe'), - display_name=pd.get('displayName'), - emojis=pd.get("emojis", {}).get("emojis"), - is_friends=pd.get("friendStatus") == "FRIENDS", - kudos=pd.get("kudos", {}).get("totalCount", -1), - profile_photo_name=pd.get('profilePhotoName'), - tags=pd.get("tags"), - user_id=pd.get('id'), - username=pd.get('username'), - ) - - def load_profile_picture(self, quality: int = 100, height: int | None = None) -> Image.Image: - """ - Loads the Profile's profile picture into memory by making an HTTP request to Lapse's servers. - :param quality: Quality of the image (1-100) - seek https://cloudinary.com/documentation/transformation_reference#q_quality for more information. - :param height: Height of the image in pixels, width is determined by image aspect ratio. Leave as None to get - original height. - - :return: Pillow image. - """ - url = f"https://image.production.journal-api.lapse.app/image/upload/q_{quality}" - url += f",h_{height}" if height is not None else "" - url += f"//{self.profile_photo_name}.jpg" - - logger.debug(f"Getting profile image from \"{url}\"") - - request = requests.get(url) - bytes_io = io.BytesIO(request.content) - image = Image.open(bytes_io) - - self.profile_picture = image - - return image - - def send_instant(self, ctx, im: Image, file_uuid: str | None = None, im_id: str | None = None, - caption: str | None = None, time_limit: int = 10): - return ctx.upload_instant(im=im, user=self, file_uuid=file_uuid, im_id=im_id, caption=caption, - time_limit=time_limit) - - def __str__(self): - return f"" - - class Snap: BASE_URL = "https://image.production.journal-api.lapse.app/image/upload/" diff --git a/lapsepy/journal/structures/profile.py b/lapsepy/journal/structures/profile.py new file mode 100644 index 0000000..bbffe8a --- /dev/null +++ b/lapsepy/journal/structures/profile.py @@ -0,0 +1,92 @@ +import io +import logging +import requests + +from datetime import datetime +from PIL import Image + +logger = logging.getLogger("lapsepy.journal.structures.py") + + +def _dt_from_iso(dt_str: str): + return datetime.fromisoformat(dt_str) + + +class Profile: + def __init__(self, user_id: str, username: str, display_name: str, profile_photo_name: str, bio: str | None, + emojis: list[str], is_friends: bool, blocked_me: bool, kudos: int, tags: list[dict], + is_blocked: bool = False, friends: list["Profile"] = None): + if friends is None: + friends = [] + + self.bio: str = bio + self.blocked_me: bool = blocked_me + self.user_display_name: str = display_name + self.emojis: list[str] = emojis + self.is_friends: bool = is_friends + self.kudos = kudos + self.profile_photo_name: str = profile_photo_name + self.tags = tags + self.user_id: str = user_id + self.username: str = username + self.media: list[Snap] = [] + self.is_blocked = is_blocked + + self.friends: list["Profile"] = friends + + self.profile_picture: Image.Image | None = None + + @staticmethod + def from_dict(profile_data: dict) -> "Profile": + """ + Generates a Profile object from a dictionary with the necessary profile data + :param profile_data: Dictionary containing the necessary data. + :return: Profile object prefilled with the data. + """ + logger.debug("Creating new Profile object from dictionary.") + + pd = profile_data + return Profile( + bio=pd.get('bio'), + blocked_me=pd.get('blockedMe'), + display_name=pd.get('displayName'), + emojis=pd.get("emojis", {}).get("emojis"), + is_friends=pd.get("friendStatus") == "FRIENDS", + kudos=pd.get("kudos", {}).get("totalCount", -1), + profile_photo_name=pd.get('profilePhotoName'), + tags=pd.get("tags"), + user_id=pd.get('id'), + username=pd.get('username'), + ) + + def load_profile_picture(self, quality: int = 100, height: int | None = None) -> Image.Image: + """ + Loads the Profile's profile picture into memory by making an HTTP request to Lapse's servers. + :param quality: Quality of the image (1-100) + seek https://cloudinary.com/documentation/transformation_reference#q_quality for more information. + :param height: Height of the image in pixels, width is determined by image aspect ratio. Leave as None to get + original height. + + :return: Pillow image. + """ + url = f"https://image.production.journal-api.lapse.app/image/upload/q_{quality}" + url += f",h_{height}" if height is not None else "" + url += f"//{self.profile_photo_name}.jpg" + + logger.debug(f"Getting profile image from \"{url}\"") + + request = requests.get(url) + bytes_io = io.BytesIO(request.content) + image = Image.open(bytes_io) + + self.profile_picture = image + + return image + + def send_instant(self, ctx, im: Image, file_uuid: str | None = None, im_id: str | None = None, + caption: str | None = None, time_limit: int = 10): + return ctx.upload_instant(im=im, user=self, file_uuid=file_uuid, im_id=im_id, caption=caption, + time_limit=time_limit) + + def __str__(self): + return f"" From 4453c6fec3a155bd4bf4863e40899179b1310382 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 19:32:20 -0400 Subject: [PATCH 08/43] Moved Snap structure to structures/snap.py --- lapsepy/journal/structures/profile.py | 2 ++ lapsepy/journal/{structures.py => structures/snap.py} | 4 ++++ 2 files changed, 6 insertions(+) rename lapsepy/journal/{structures.py => structures/snap.py} (98%) diff --git a/lapsepy/journal/structures/profile.py b/lapsepy/journal/structures/profile.py index bbffe8a..01ef760 100644 --- a/lapsepy/journal/structures/profile.py +++ b/lapsepy/journal/structures/profile.py @@ -5,6 +5,8 @@ from datetime import datetime from PIL import Image +from .snap import Snap + logger = logging.getLogger("lapsepy.journal.structures.py") diff --git a/lapsepy/journal/structures.py b/lapsepy/journal/structures/snap.py similarity index 98% rename from lapsepy/journal/structures.py rename to lapsepy/journal/structures/snap.py index 103c22c..ff7a748 100644 --- a/lapsepy/journal/structures.py +++ b/lapsepy/journal/structures/snap.py @@ -13,6 +13,10 @@ logger = logging.getLogger("lapsepy.journal.structures.py") +def _dt_from_iso(dt_str: str): + return datetime.fromisoformat(dt_str) + + class Snap: BASE_URL = "https://image.production.journal-api.lapse.app/image/upload/" From 08dc5f176e810dfe52adde5b08d9801817baf0bb Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 19:34:55 -0400 Subject: [PATCH 09/43] Fixed import statements --- lapsepy/journal/journal.py | 3 ++- lapsepy/lapse/lapse.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lapsepy/journal/journal.py b/lapsepy/journal/journal.py index 4c9db44..a4f5257 100644 --- a/lapsepy/journal/journal.py +++ b/lapsepy/journal/journal.py @@ -18,7 +18,8 @@ from lapsepy.journal.factory.profile_factory import SaveBioGQL, SaveDisplayNameGQL, SaveUsernameGQL, SaveEmojisGQL, \ SaveDOBGQL -from .structures import Profile, Snap +from .structures.snap import Snap +from .structures.profile import Profile import logging diff --git a/lapsepy/lapse/lapse.py b/lapsepy/lapse/lapse.py index 8f2cf61..1177ada 100644 --- a/lapsepy/lapse/lapse.py +++ b/lapsepy/lapse/lapse.py @@ -8,7 +8,7 @@ from lapsepy.auth.refresher import refresh from lapsepy.journal.journal import Journal from lapsepy.journal.common.exceptions import AuthTokenExpired -from lapsepy.journal.structures import Profile +from lapsepy.journal.structures.profile import Profile import logging From 0c15d863cb4159be54e9b57ef12e7ca7a12fc8b7 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 19:42:54 -0400 Subject: [PATCH 10/43] Removed print statement from debugging --- lapsepy/journal/journal.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lapsepy/journal/journal.py b/lapsepy/journal/journal.py index f9668d7..5988a0a 100644 --- a/lapsepy/journal/journal.py +++ b/lapsepy/journal/journal.py @@ -164,7 +164,6 @@ def upload_instant(self, im: Image.Image, user_id: str, file_uuid: str | None = if file_uuid is None: # UUID in testing always started with "01HDCWT" with a total length of 26 chars. file_uuid = "01HDCWT" + str(uuid4()).upper().replace("-", "")[:19] - print(file_uuid) if im_id is None: # UUID in testing always started with "01HDCWT" with a total length of 26 chars. From 32ef441bdb4e94c7ae45d2ea5bfff6b3854726a1 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 19:43:26 -0400 Subject: [PATCH 11/43] Removed print statement from debugging --- lapsepy/journal/journal.py | 1 - 1 file changed, 1 deletion(-) diff --git a/lapsepy/journal/journal.py b/lapsepy/journal/journal.py index f9668d7..5988a0a 100644 --- a/lapsepy/journal/journal.py +++ b/lapsepy/journal/journal.py @@ -164,7 +164,6 @@ def upload_instant(self, im: Image.Image, user_id: str, file_uuid: str | None = if file_uuid is None: # UUID in testing always started with "01HDCWT" with a total length of 26 chars. file_uuid = "01HDCWT" + str(uuid4()).upper().replace("-", "")[:19] - print(file_uuid) if im_id is None: # UUID in testing always started with "01HDCWT" with a total length of 26 chars. From 18d891c7467383920b77bf823f5d340e8ef6dc26 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 20:04:07 -0400 Subject: [PATCH 12/43] Created friendsfeed structures --- lapsepy/journal/factory/friends_factory.py | 6 +-- lapsepy/journal/journal.py | 58 ++++------------------ lapsepy/journal/structures/__init__.py | 8 +++ lapsepy/journal/structures/friendsfeed.py | 18 +++++++ 4 files changed, 39 insertions(+), 51 deletions(-) create mode 100644 lapsepy/journal/structures/__init__.py create mode 100644 lapsepy/journal/structures/friendsfeed.py diff --git a/lapsepy/journal/factory/friends_factory.py b/lapsepy/journal/factory/friends_factory.py index 98af07c..03f4541 100644 --- a/lapsepy/journal/factory/friends_factory.py +++ b/lapsepy/journal/factory/friends_factory.py @@ -11,7 +11,7 @@ class FriendsFeedItemsGQL(BaseGQL): Gets items from friends feed. """ - def __init__(self, start_cursor: str | None = None): + def __init__(self, last: int = 10): super().__init__( operation_name="FriendsFeedItemsGraphQLQuery", query="query FriendsFeedItemsGraphQLQuery($first: Int, $after: String, $last: Int, $before: String) { " @@ -83,8 +83,8 @@ def __init__(self, start_cursor: str | None = None): "FriendsFeedItemTaggedMediaSharedV2 { __typename sharedMedia { __typename " "...FriendsFeedItemMediaSharedDetails } }") - self.last = 10 - self.before = start_cursor + self.last = last + self.before = None self.variables = {} diff --git a/lapsepy/journal/journal.py b/lapsepy/journal/journal.py index b3c832d..63eacec 100644 --- a/lapsepy/journal/journal.py +++ b/lapsepy/journal/journal.py @@ -4,6 +4,7 @@ """ import io +import json from .common.exceptions import sync_journal_exception_router, SyncJournalException @@ -18,8 +19,7 @@ from lapsepy.journal.factory.profile_factory import SaveBioGQL, SaveDisplayNameGQL, SaveUsernameGQL, SaveEmojisGQL, \ SaveDOBGQL -from .structures.snap import Snap -from .structures.profile import Profile +from .structures import Snap, Profile, FriendsFeed import logging @@ -190,8 +190,6 @@ def send_kudos(self, user_id: str): if not response.get("data", {}).get("sendKudos", {}).get("success"): raise SyncJournalException("Error sending kudos, could you already have reached your daily limit?") - - def get_friends_feed(self, count: int = 10) -> list[Profile]: """ Gets your friend upload feed. @@ -199,50 +197,14 @@ def get_friends_feed(self, count: int = 10) -> list[Profile]: :return: A list of profiles """ - cursor = None - - profiles = {} - entry_ids = [] - - # If it started to repeat itself. - maxed = False - for _ in range(1, count, 10): - logger.debug(f"Getting friends feed starting from cursor: {cursor or 'INITIAL'}") - query = FriendsFeedItemsGQL(cursor).to_dict() - response = self._sync_journal_call(query) - - # Where to query the new data from - cursor = response['data']['friendsFeedItems']['pageInfo']['endCursor'] - if cursor is None: - logger.debug("Reached max cursor depth.") - break - - # Trim useless data from response - feed_data = [i['node'] for i in response['data']['friendsFeedItems']['edges']] - - # Create Profile objects which hold the media data in Profile.media - for node in feed_data: - username = node.get('user').get('username') - if username in profiles.keys(): - profile = profiles[username] - else: - profile = Profile.from_dict(node.get("user")) - profiles[username] = profile - - for entry in node['content']['entries']: - eid = entry['id'] - if eid in entry_ids: - logger.warn("Found duplicate of media, must've reached the end already...") - maxed = True - break - entry_ids.append(eid) - snap = Snap.from_dict(entry) - profile.media.append(snap) - - if maxed: - break - - return list(profiles.values()) + nodes = [] + + # Get all the user's friends in the range. + query = FriendsFeedItemsGQL(last=count).to_dict() + response = self._sync_journal_call(query) + + nodes = [i['node'] for i in response['data']['friendsFeedItems']['edges']] + def get_profile_by_id(self, user_id: str, album_limit: int = 6, friends_limit: int = 10) -> Profile: """ diff --git a/lapsepy/journal/structures/__init__.py b/lapsepy/journal/structures/__init__.py new file mode 100644 index 0000000..5a24625 --- /dev/null +++ b/lapsepy/journal/structures/__init__.py @@ -0,0 +1,8 @@ +""" +Author: Quintin Dunn +Date: 10/27/23 +""" + +from .profile import Profile +from .snap import Snap +from .friendsfeed import FriendsFeed diff --git a/lapsepy/journal/structures/friendsfeed.py b/lapsepy/journal/structures/friendsfeed.py new file mode 100644 index 0000000..a4c8a81 --- /dev/null +++ b/lapsepy/journal/structures/friendsfeed.py @@ -0,0 +1,18 @@ +from datetime import datetime + +from .profile import Profile + + +def _dt_from_iso(dt_str: str): + return datetime.fromisoformat(dt_str) + + +class FriendsFeed: + def __init__(self, nodes: list["FriendNode"]): + self.nodes: list[FriendNode] = nodes + + +class FriendNode: + def __init__(self, profile: Profile, iso_string: str): + self.profile = profile + self.timestamp: datetime = _dt_from_iso(iso_string) From 2e3108c10617c8b03ab87f10a87db2a3e0f8f083 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 20:11:20 -0400 Subject: [PATCH 13/43] Added FriendsFeed object generation --- lapsepy/journal/journal.py | 25 +++++++++++++++++++---- lapsepy/journal/structures/__init__.py | 2 +- lapsepy/journal/structures/friendsfeed.py | 4 +++- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/lapsepy/journal/journal.py b/lapsepy/journal/journal.py index 63eacec..1b84426 100644 --- a/lapsepy/journal/journal.py +++ b/lapsepy/journal/journal.py @@ -4,7 +4,6 @@ """ import io -import json from .common.exceptions import sync_journal_exception_router, SyncJournalException @@ -19,7 +18,7 @@ from lapsepy.journal.factory.profile_factory import SaveBioGQL, SaveDisplayNameGQL, SaveUsernameGQL, SaveEmojisGQL, \ SaveDOBGQL -from .structures import Snap, Profile, FriendsFeed +from .structures import Snap, Profile, FriendsFeed, FriendNode import logging @@ -190,7 +189,7 @@ def send_kudos(self, user_id: str): if not response.get("data", {}).get("sendKudos", {}).get("success"): raise SyncJournalException("Error sending kudos, could you already have reached your daily limit?") - def get_friends_feed(self, count: int = 10) -> list[Profile]: + def get_friends_feed(self, count: int = 10) -> FriendsFeed: """ Gets your friend upload feed. :param count: How many collection to grab. @@ -203,8 +202,26 @@ def get_friends_feed(self, count: int = 10) -> list[Profile]: query = FriendsFeedItemsGQL(last=count).to_dict() response = self._sync_journal_call(query) - nodes = [i['node'] for i in response['data']['friendsFeedItems']['edges']] + nodes: list[dict] = [i['node'] for i in response['data']['friendsFeedItems']['edges']] + friend_nodes = [] + + for node in nodes: + profile_data = node.get("user") + profile = Profile.from_dict(profile_data) + + timestamp = node.get("timestamp", {}).get("isoString") + + entries = node.get("content").get("entries") + + node_entry_objs = [] + for entry in entries: + snap = Snap.from_dict(entry) + node_entry_objs.append(snap) + + friend_nodes.append(FriendNode(profile=profile, iso_string=timestamp, entries=node_entry_objs)) + + return FriendsFeed(friend_nodes) def get_profile_by_id(self, user_id: str, album_limit: int = 6, friends_limit: int = 10) -> Profile: """ diff --git a/lapsepy/journal/structures/__init__.py b/lapsepy/journal/structures/__init__.py index 5a24625..e6b996d 100644 --- a/lapsepy/journal/structures/__init__.py +++ b/lapsepy/journal/structures/__init__.py @@ -5,4 +5,4 @@ from .profile import Profile from .snap import Snap -from .friendsfeed import FriendsFeed +from .friendsfeed import FriendsFeed, FriendNode diff --git a/lapsepy/journal/structures/friendsfeed.py b/lapsepy/journal/structures/friendsfeed.py index a4c8a81..7a3a23f 100644 --- a/lapsepy/journal/structures/friendsfeed.py +++ b/lapsepy/journal/structures/friendsfeed.py @@ -1,6 +1,7 @@ from datetime import datetime from .profile import Profile +from .snap import Snap def _dt_from_iso(dt_str: str): @@ -13,6 +14,7 @@ def __init__(self, nodes: list["FriendNode"]): class FriendNode: - def __init__(self, profile: Profile, iso_string: str): + def __init__(self, profile: Profile, iso_string: str, entries: list[Snap]): self.profile = profile self.timestamp: datetime = _dt_from_iso(iso_string) + self.entries: list[Snap] = entries From b8a1ae8573856cfdfbfa05ac2ae16dde669b822d Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 20:11:54 -0400 Subject: [PATCH 14/43] Removed redundant nodes list decleration --- lapsepy/journal/journal.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lapsepy/journal/journal.py b/lapsepy/journal/journal.py index 1b84426..330643d 100644 --- a/lapsepy/journal/journal.py +++ b/lapsepy/journal/journal.py @@ -196,8 +196,6 @@ def get_friends_feed(self, count: int = 10) -> FriendsFeed: :return: A list of profiles """ - nodes = [] - # Get all the user's friends in the range. query = FriendsFeedItemsGQL(last=count).to_dict() response = self._sync_journal_call(query) From 21f6ea919dc6f627a736332b66763acff03e0bea Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 20:12:57 -0400 Subject: [PATCH 15/43] Made FriendsFeed iterable --- lapsepy/journal/structures/friendsfeed.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lapsepy/journal/structures/friendsfeed.py b/lapsepy/journal/structures/friendsfeed.py index 7a3a23f..34429f6 100644 --- a/lapsepy/journal/structures/friendsfeed.py +++ b/lapsepy/journal/structures/friendsfeed.py @@ -12,6 +12,9 @@ class FriendsFeed: def __init__(self, nodes: list["FriendNode"]): self.nodes: list[FriendNode] = nodes + def __iter__(self): + return iter(self.nodes) + class FriendNode: def __init__(self, profile: Profile, iso_string: str, entries: list[Snap]): From a20f9fff1a790b59ba79c31d0c0b3872bca78ada Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 20:15:02 -0400 Subject: [PATCH 16/43] Added automatically adding of snaps to profile --- lapsepy/journal/journal.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lapsepy/journal/journal.py b/lapsepy/journal/journal.py index 330643d..8dac1e4 100644 --- a/lapsepy/journal/journal.py +++ b/lapsepy/journal/journal.py @@ -216,6 +216,7 @@ def get_friends_feed(self, count: int = 10) -> FriendsFeed: for entry in entries: snap = Snap.from_dict(entry) node_entry_objs.append(snap) + profile.media.append(snap) friend_nodes.append(FriendNode(profile=profile, iso_string=timestamp, entries=node_entry_objs)) From 2c004dc61c00591c1f3e9a6ca78527f78e6d78c8 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 20:17:37 -0400 Subject: [PATCH 17/43] Updated examples/get_friends_feed/* --- examples/get_friends_feed/get_friends_feed_1.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/get_friends_feed/get_friends_feed_1.py b/examples/get_friends_feed/get_friends_feed_1.py index 3639ae4..4fe8374 100644 --- a/examples/get_friends_feed/get_friends_feed_1.py +++ b/examples/get_friends_feed/get_friends_feed_1.py @@ -11,10 +11,12 @@ if not os.path.isdir("./out"): os.mkdir("./out") - for profile in friends_feed: + for friend_node in friends_feed: + profile = friend_node.profile profile.load_profile_picture(quality=100, height=None) profile.profile_picture.save(f"./out/{profile.username}.jpg") - for snap in profile.media: - snap.load_snap(quality=100, fl_keep_iptc=True) - save_path = f"./out/{snap.filtered_id.replace('/', '_')}.jpg" - snap.filtered.save(save_path) + for entry in friend_node.entries: + entry.load_snap(quality=100, fl_keep_iptc=True) + save_path = f"./out/{entry.filtered_id.replace('/', '_')}.jpg" + entry.filtered.save(save_path) + From c36623687072037e081d9ae21e49d37bda69829d Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 20:35:32 -0400 Subject: [PATCH 18/43] Added music to profile from get_profile_by_id method --- lapsepy/journal/journal.py | 15 ++++++++++++++- lapsepy/journal/structures/__init__.py | 2 +- lapsepy/journal/structures/profile.py | 12 +++++++++++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/lapsepy/journal/journal.py b/lapsepy/journal/journal.py index 8dac1e4..0887113 100644 --- a/lapsepy/journal/journal.py +++ b/lapsepy/journal/journal.py @@ -18,7 +18,7 @@ from lapsepy.journal.factory.profile_factory import SaveBioGQL, SaveDisplayNameGQL, SaveUsernameGQL, SaveEmojisGQL, \ SaveDOBGQL -from .structures import Snap, Profile, FriendsFeed, FriendNode +from .structures import Snap, Profile, ProfileMusic, FriendsFeed, FriendNode import logging @@ -241,6 +241,18 @@ def get_profile_by_id(self, user_id: str, album_limit: int = 6, friends_limit: i pd = response.get("data", {}).get("profile", {}) def generate_profile_object(profile_data: dict) -> Profile: + music = profile_data.get("music", {}) + if music is not None: + profile_music = ProfileMusic( + artist=music.get("artist"), + artwork_url=music.get("artwork"), + duration=music.get("duration"), + song_title=music.get("songTitle"), + song_url=music.get("songUrl") + ) + else: + profile_music = None + return Profile( bio=profile_data.get('bio'), blocked_me=profile_data.get('blockedMe'), @@ -253,6 +265,7 @@ def generate_profile_object(profile_data: dict) -> Profile: tags=profile_data.get("tags"), user_id=profile_data.get('id'), username=profile_data.get('username'), + profile_music=profile_music ) profile = generate_profile_object(pd) diff --git a/lapsepy/journal/structures/__init__.py b/lapsepy/journal/structures/__init__.py index e6b996d..c54c325 100644 --- a/lapsepy/journal/structures/__init__.py +++ b/lapsepy/journal/structures/__init__.py @@ -3,6 +3,6 @@ Date: 10/27/23 """ -from .profile import Profile +from .profile import Profile, ProfileMusic from .snap import Snap from .friendsfeed import FriendsFeed, FriendNode diff --git a/lapsepy/journal/structures/profile.py b/lapsepy/journal/structures/profile.py index 01ef760..53fee82 100644 --- a/lapsepy/journal/structures/profile.py +++ b/lapsepy/journal/structures/profile.py @@ -17,7 +17,7 @@ def _dt_from_iso(dt_str: str): class Profile: def __init__(self, user_id: str, username: str, display_name: str, profile_photo_name: str, bio: str | None, emojis: list[str], is_friends: bool, blocked_me: bool, kudos: int, tags: list[dict], - is_blocked: bool = False, friends: list["Profile"] = None): + is_blocked: bool = False, friends: list["Profile"] = None, profile_music: "ProfileMusic" = None): if friends is None: friends = [] @@ -35,6 +35,7 @@ def __init__(self, user_id: str, username: str, display_name: str, profile_photo self.is_blocked = is_blocked self.friends: list["Profile"] = friends + self.profile_music = profile_music self.profile_picture: Image.Image | None = None @@ -92,3 +93,12 @@ def send_instant(self, ctx, im: Image, file_uuid: str | None = None, im_id: str def __str__(self): return f"" + + +class ProfileMusic: + def __init__(self, artist: str, artwork_url: str, duration: int, song_title: str, song_url: str): + self.artist = artist + self.artwork_url = artwork_url + self.duration = duration + self.song_title = song_title + self.song_url = song_url From 446ca78c3ff7e31d19418435f9b7b4135a10ac1b Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 20:38:40 -0400 Subject: [PATCH 19/43] Added music to profile from Profile.from_dict method --- lapsepy/journal/structures/profile.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lapsepy/journal/structures/profile.py b/lapsepy/journal/structures/profile.py index 53fee82..b44d4f7 100644 --- a/lapsepy/journal/structures/profile.py +++ b/lapsepy/journal/structures/profile.py @@ -49,6 +49,19 @@ def from_dict(profile_data: dict) -> "Profile": logger.debug("Creating new Profile object from dictionary.") pd = profile_data + + music = pd.get("music", {}) + if music is not None: + profile_music = ProfileMusic( + artist=music.get("artist"), + artwork_url=music.get("artwork"), + duration=music.get("duration"), + song_title=music.get("songTitle"), + song_url=music.get("songUrl") + ) + else: + profile_music = None + return Profile( bio=pd.get('bio'), blocked_me=pd.get('blockedMe'), @@ -60,6 +73,7 @@ def from_dict(profile_data: dict) -> "Profile": tags=pd.get("tags"), user_id=pd.get('id'), username=pd.get('username'), + profile_music=profile_music ) def load_profile_picture(self, quality: int = 100, height: int | None = None) -> Image.Image: From bdf70745a6278dc12ff13689b34beca1eb5b37a5 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 20:51:27 -0400 Subject: [PATCH 20/43] Fixed retrieval of artworkUrl and updated examples --- .../get_friends_feed/get_friends_feed_1.py | 18 +++++++++++++ lapsepy/journal/journal.py | 2 +- lapsepy/journal/structures/profile.py | 25 ++++++++++++++++++- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/examples/get_friends_feed/get_friends_feed_1.py b/examples/get_friends_feed/get_friends_feed_1.py index 4fe8374..0d51b86 100644 --- a/examples/get_friends_feed/get_friends_feed_1.py +++ b/examples/get_friends_feed/get_friends_feed_1.py @@ -13,10 +13,28 @@ for friend_node in friends_feed: profile = friend_node.profile + + # Get profile picture profile.load_profile_picture(quality=100, height=None) profile.profile_picture.save(f"./out/{profile.username}.jpg") + + # Get all images from collections for entry in friend_node.entries: entry.load_snap(quality=100, fl_keep_iptc=True) save_path = f"./out/{entry.filtered_id.replace('/', '_')}.jpg" entry.filtered.save(save_path) + # Get profile music if user has profile music. + if profile.profile_music is not None: + profile.profile_music.load() + profile_music = profile.profile_music + + # Save artwork if exists + if profile_music.artwork: + save_path = f"./out/{profile.username}_music.png" + profile_music.artwork.save(save_path) + + # Save song + save_path = f"./out/{profile.username}_music.mp3" + with open(save_path, 'wb') as f: + f.write(profile_music.song) diff --git a/lapsepy/journal/journal.py b/lapsepy/journal/journal.py index 0887113..7833450 100644 --- a/lapsepy/journal/journal.py +++ b/lapsepy/journal/journal.py @@ -245,7 +245,7 @@ def generate_profile_object(profile_data: dict) -> Profile: if music is not None: profile_music = ProfileMusic( artist=music.get("artist"), - artwork_url=music.get("artwork"), + artwork_url=music.get("artworkUrl"), duration=music.get("duration"), song_title=music.get("songTitle"), song_url=music.get("songUrl") diff --git a/lapsepy/journal/structures/profile.py b/lapsepy/journal/structures/profile.py index b44d4f7..b22b6f1 100644 --- a/lapsepy/journal/structures/profile.py +++ b/lapsepy/journal/structures/profile.py @@ -3,7 +3,9 @@ import requests from datetime import datetime + from PIL import Image +import io from .snap import Snap @@ -54,7 +56,7 @@ def from_dict(profile_data: dict) -> "Profile": if music is not None: profile_music = ProfileMusic( artist=music.get("artist"), - artwork_url=music.get("artwork"), + artwork_url=music.get("artworkUrl"), duration=music.get("duration"), song_title=music.get("songTitle"), song_url=music.get("songUrl") @@ -116,3 +118,24 @@ def __init__(self, artist: str, artwork_url: str, duration: int, song_title: str self.duration = duration self.song_title = song_title self.song_url = song_url + + self.song: None | bytes = None + self.artwork: None | Image.Image = None + + def load(self): + """ + Loads the song, and artwork into memory + :return: None + """ + # Get song + request = requests.get(self.song_url) + request.raise_for_status() + self.song = request.content + + # Get artwork + if self.artwork_url: + request = requests.get(self.artwork_url) + request.raise_for_status() + + bytes_io = io.BytesIO(request.content) + self.artwork = Image.open(bytes_io) \ No newline at end of file From 532958e29be79c7b9c536ea29e0d339e52e3f498 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 21:03:56 -0400 Subject: [PATCH 21/43] Bumped version to 0.2.0 --- lapsepy/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lapsepy/__init__.py b/lapsepy/__init__.py index 51bd283..b9cd1a7 100644 --- a/lapsepy/__init__.py +++ b/lapsepy/__init__.py @@ -3,7 +3,7 @@ Date: 10/22/23 """ -__version__ = '0.1.1' +__version__ = '0.2.0' from .journal import Journal from .auth.refresher import refresh diff --git a/setup.py b/setup.py index 2e61bab..9937ce9 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages -VERSION = "0.1.1" +VERSION = "0.2.0" DESCRIPTION = "A Python API wrapper for the social media app Lapse." with open("README.md", 'r') as f: From 91b7c0c9878ef65a9e317e6f81a4df60dde66972 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 21:07:38 -0400 Subject: [PATCH 22/43] Fixed setup.py for pypi --- README.md | 2 +- setup.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index de65332..194404f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![LapsePy](./icon.png) +![LapsePy](https://github.com/quintindunn/lapsepy/blob/main/icon.png?raw=true)
diff --git a/setup.py b/setup.py index 9937ce9..ce3f461 100644 --- a/setup.py +++ b/setup.py @@ -9,8 +9,7 @@ with open("requirements.txt", 'r', encoding="utf-16") as f: requirements = [i.strip() for i in f.readlines()] -with open("LICENSE", 'r') as f: - LICENSE = f.read() +LICENSE = "MIT" setup(name='lapsepy', version=VERSION, From 0599159889b9d139d0ded776cb8b356176da6ae1 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Fri, 27 Oct 2023 21:08:18 -0400 Subject: [PATCH 23/43] Bumped version to 0.2.1 --- lapsepy/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lapsepy/__init__.py b/lapsepy/__init__.py index b9cd1a7..8af1e1d 100644 --- a/lapsepy/__init__.py +++ b/lapsepy/__init__.py @@ -3,7 +3,7 @@ Date: 10/22/23 """ -__version__ = '0.2.0' +__version__ = '0.2.1' from .journal import Journal from .auth.refresher import refresh diff --git a/setup.py b/setup.py index ce3f461..27430d5 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from setuptools import setup, find_packages -VERSION = "0.2.0" +VERSION = "0.2.1" DESCRIPTION = "A Python API wrapper for the social media app Lapse." with open("README.md", 'r') as f: From 43cc82f76e4668b5f49a91f2b39ddba49a5c50f8 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 10:32:00 -0500 Subject: [PATCH 24/43] Added StatusUpdateGQL --- lapsepy/journal/factory/media_factory.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lapsepy/journal/factory/media_factory.py b/lapsepy/journal/factory/media_factory.py index 20a8f1a..ca2f4b9 100644 --- a/lapsepy/journal/factory/media_factory.py +++ b/lapsepy/journal/factory/media_factory.py @@ -125,3 +125,20 @@ def _render_variables(self): self.variables["filename"] = f"{self.file_uuid}/filtered_0.heic" else: self.variables["filename"] = f"instant/{self.file_uuid}.heic" + + +class StatusUpdateGQL(BaseGQL): + def __init__(self, text: str, msg_id: str): + super().__init__("CreateStatusUpdateGraphQLMutation", + "mutation CreateStatusUpdateGraphQLMutation($input: CreateStatusUpdateInput!) " + "{ createStatusUpdate(input: $input) { __typename success } }") + self.text = text + self.msg_id = msg_id + + def _render_variables(self): + self.variables['input'] = { + "body": { + "text": self.text + }, + "id": self.msg_id + } From 91a4ffd4cbcc0e21d1328d074b8e36c08d6b8dec Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 10:37:43 -0500 Subject: [PATCH 25/43] Created driving functions in journal.py & lapse.py --- .../create_status_update_01.py | 0 lapsepy/journal/journal.py | 18 +++++++++++++++++- lapsepy/lapse/lapse.py | 9 +++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 examples/create_status_update/create_status_update_01.py diff --git a/examples/create_status_update/create_status_update_01.py b/examples/create_status_update/create_status_update_01.py new file mode 100644 index 0000000..e69de29 diff --git a/lapsepy/journal/journal.py b/lapsepy/journal/journal.py index 7833450..d88f766 100644 --- a/lapsepy/journal/journal.py +++ b/lapsepy/journal/journal.py @@ -4,6 +4,7 @@ """ import io +import uuid from .common.exceptions import sync_journal_exception_router, SyncJournalException @@ -14,7 +15,7 @@ import requests from .factory.friends_factory import FriendsFeedItemsGQL, ProfileDetailsGQL, SendKudosGQL -from .factory.media_factory import ImageUploadURLGQL, CreateMediaGQL, SendInstantsGQL +from .factory.media_factory import ImageUploadURLGQL, CreateMediaGQL, SendInstantsGQL, StatusUpdateGQL from lapsepy.journal.factory.profile_factory import SaveBioGQL, SaveDisplayNameGQL, SaveUsernameGQL, SaveEmojisGQL, \ SaveDOBGQL @@ -177,6 +178,21 @@ def upload_instant(self, im: Image.Image, user_id: str, file_uuid: str | None = time_limit=time_limit).to_dict() self._sync_journal_call(query) + def create_status_update(self, text: str, msg_id: str | None): + """ + Creates a status update on your Journal + :param text: Msg of the text to send + :param msg_id: Leave None if you don't know what you're doing. FORMAT: STATUS_UPDATE:<(str(uuid.uuid4))> + :return: + """ + if msg_id is None: + msg_id = f"STATUS_UPDATE:{uuid.uuid4()}" + query = StatusUpdateGQL(text=text, msg_id=msg_id).to_dict() + response = self._sync_journal_call(query) + + if not response.get("data", {}).get("createStatusUpdate", {}).get("success"): + raise SyncJournalException("Error create new status.") + def send_kudos(self, user_id: str): """ Sends kudos (vibes) to a given user diff --git a/lapsepy/lapse/lapse.py b/lapsepy/lapse/lapse.py index 1177ada..3ddf2b0 100644 --- a/lapsepy/lapse/lapse.py +++ b/lapsepy/lapse/lapse.py @@ -86,6 +86,15 @@ def upload_instant(self, im: Image, user: str | Profile, file_uuid: str | None = return self.journal.upload_instant(im=im, user_id=user, file_uuid=file_uuid, im_id=im_id, caption=caption, time_limit=time_limit) + def create_status_update(self, text: str, msg_id: str | None = None): + """ + Creates a status update on your Journal + :param text: Msg of the text to send + :param msg_id: Leave None if you don't know what you're doing. FORMAT: STATUS_UPDATE:<(str(uuid.uuid4))> + :return: + """ + return self.journal.create_status_update(text=text, msg_id=msg_id) + def send_kudos(self, user: str | Profile): """ Sends kudos (vibes) to a user. From 61254a3881c412dff73cd4cb83f117cb09b4d3b9 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 10:42:30 -0500 Subject: [PATCH 26/43] Wrapped up adding status updates --- examples/create_status_update/create_status_update_01.py | 9 +++++++++ lapsepy/journal/factory/media_factory.py | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/examples/create_status_update/create_status_update_01.py b/examples/create_status_update/create_status_update_01.py index e69de29..a98accc 100644 --- a/examples/create_status_update/create_status_update_01.py +++ b/examples/create_status_update/create_status_update_01.py @@ -0,0 +1,9 @@ +import os + +from lapsepy.lapse import Lapse + +if __name__ == '__main__': + lapse = Lapse(os.getenv("REFRESH_TOKEN")) + + msg = input("Message: ") + lapse.create_status_update(text=msg) diff --git a/lapsepy/journal/factory/media_factory.py b/lapsepy/journal/factory/media_factory.py index ca2f4b9..1b6eed7 100644 --- a/lapsepy/journal/factory/media_factory.py +++ b/lapsepy/journal/factory/media_factory.py @@ -135,6 +135,10 @@ def __init__(self, text: str, msg_id: str): self.text = text self.msg_id = msg_id + self.variables = {} + + self._render_variables() + def _render_variables(self): self.variables['input'] = { "body": { From ae826674409d88d7502086a803ca8db649a1106a Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 10:47:41 -0500 Subject: [PATCH 27/43] Added RemoveFriendFeedItemGQL --- lapsepy/journal/factory/media_factory.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lapsepy/journal/factory/media_factory.py b/lapsepy/journal/factory/media_factory.py index 1b6eed7..c62489a 100644 --- a/lapsepy/journal/factory/media_factory.py +++ b/lapsepy/journal/factory/media_factory.py @@ -146,3 +146,24 @@ def _render_variables(self): }, "id": self.msg_id } + + +class RemoveFriendsFeedItemGQL(BaseGQL): + def __init__(self, msg_id: str, iso_string: str): + super().__init__("RemoveFriendsFeedItem", + "mutation RemoveFriendsFeedItem($input: RemoveFriendsFeedItemInput!) " + "{ removeFriendsFeedItem(input: $input) { __typename success } }") + self.msg_id = msg_id + self.iso_string = iso_string + + self.variables = {} + + self._render_variables() + + def _render_variables(self): + self.variables['input'] = { + "id": self.msg_id, + "removedAt": { + "isoString": self.iso_string + } + } From 5c3946c1a295ec5e0a33da70197a9842406f8c00 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 10:51:31 -0500 Subject: [PATCH 28/43] Added remove_status_update in journal --- lapsepy/journal/journal.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lapsepy/journal/journal.py b/lapsepy/journal/journal.py index d88f766..f7681a0 100644 --- a/lapsepy/journal/journal.py +++ b/lapsepy/journal/journal.py @@ -15,7 +15,8 @@ import requests from .factory.friends_factory import FriendsFeedItemsGQL, ProfileDetailsGQL, SendKudosGQL -from .factory.media_factory import ImageUploadURLGQL, CreateMediaGQL, SendInstantsGQL, StatusUpdateGQL +from .factory.media_factory import ImageUploadURLGQL, CreateMediaGQL, SendInstantsGQL, StatusUpdateGQL, \ + RemoveFriendsFeedItemGQL from lapsepy.journal.factory.profile_factory import SaveBioGQL, SaveDisplayNameGQL, SaveUsernameGQL, SaveEmojisGQL, \ SaveDOBGQL @@ -193,6 +194,23 @@ def create_status_update(self, text: str, msg_id: str | None): if not response.get("data", {}).get("createStatusUpdate", {}).get("success"): raise SyncJournalException("Error create new status.") + def remove_status_update(self, msg_id: str, removed_at: datetime | None): + """ + Removes a status update + :param msg_id: ID of the status update + :param removed_at: datetime object of when it was removed + :return: + """ + if removed_at is None: + removed_at = datetime.now() + removed_at = format_iso_time(removed_at) + + query = RemoveFriendsFeedItemGQL(msg_id=msg_id, iso_string=removed_at).to_dict() + response = self._sync_journal_call(query) + + if not response.get("data", {}).get("removeFriendsFeedItem", {}).get("success"): + raise SyncJournalException("Failed removing status.") + def send_kudos(self, user_id: str): """ Sends kudos (vibes) to a given user From 80a4c25ad5706569acb1bae3a8ac3b84c48376ae Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 10:52:01 -0500 Subject: [PATCH 29/43] Added remove_status_update to lapse.py --- lapsepy/lapse/lapse.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lapsepy/lapse/lapse.py b/lapsepy/lapse/lapse.py index 3ddf2b0..2a69242 100644 --- a/lapsepy/lapse/lapse.py +++ b/lapsepy/lapse/lapse.py @@ -95,6 +95,15 @@ def create_status_update(self, text: str, msg_id: str | None = None): """ return self.journal.create_status_update(text=text, msg_id=msg_id) + def remove_status_update(self, msg_id: str, removed_at: datetime | None = None): + """ + Removes a status update + :param msg_id: ID of the status update + :param removed_at: datetime object of when it was removed + :return: + """ + return self.journal.remove_status_update(msg_id=msg_id, removed_at=removed_at) + def send_kudos(self, user: str | Profile): """ Sends kudos (vibes) to a user. From d5d4e263f7bf3f07fe6144802102ec24d48faca7 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 10:56:28 -0500 Subject: [PATCH 30/43] Added remove example 1 --- examples/remove_status_update/remove_status_update_01.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 examples/remove_status_update/remove_status_update_01.py diff --git a/examples/remove_status_update/remove_status_update_01.py b/examples/remove_status_update/remove_status_update_01.py new file mode 100644 index 0000000..cd379e6 --- /dev/null +++ b/examples/remove_status_update/remove_status_update_01.py @@ -0,0 +1,9 @@ +import os + +from lapsepy import Lapse + +if __name__ == '__main__': + lapse = Lapse(os.getenv("REFRESH_TOKEN")) + + msg_to_remove = input("MSG ID: ") + lapse.remove_status_update(msg_to_remove) From afedc49a5409154953745fbb533dcdcb89f94811 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 11:13:48 -0500 Subject: [PATCH 31/43] Updated GQL query --- lapsepy/journal/factory/friends_factory.py | 94 ++++++++++++---------- 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/lapsepy/journal/factory/friends_factory.py b/lapsepy/journal/factory/friends_factory.py index 03f4541..5d88831 100644 --- a/lapsepy/journal/factory/friends_factory.py +++ b/lapsepy/journal/factory/friends_factory.py @@ -31,57 +31,69 @@ def __init__(self, last: int = 10): "FriendsFeedItemProfileCompletedV1 { ...FriendsFeedItemProfileCompletedDetails } ... on " "FriendsFeedItemProfilePhotoUpdatedV1 { ...FriendsFeedItemProfilePhotoUpdatedDetails } ... on " "FriendsFeedItemSelectsUpdatedV1 { ...FriendsFeedItemSelectsUpdatedDetails } ... on " + "FriendsFeedItemStatusUpdatedV1 { ...FriendsFeedItemStatusUpdatedDetails } ... on " "FriendsFeedItemTaggedMediaSharedV2 { ...FriendsFeedItemTaggedMediaSharedDetails } } comments(" - "first: 3) { __typename edges { __typename node { __typename ...MediaCommentDetails } } totalCount " - "} reactions { __typename ...MediaReactionDetails } user { __typename ...ProfileDetails } timestamp " - "{ __typename isoString } }\nfragment FriendsFeedItemAlbumUpdatedDetails on " - "FriendsFeedItemAlbumUpdatedV1 { __typename id title mediaIds totalCount }\nfragment " - "FriendsFeedItemBioUpdatedDetails on FriendsFeedItemBioUpdatedV1 { __typename bio }\nfragment " - "FriendsFeedItemEmojisUpdatedDetails on FriendsFeedItemEmojisUpdatedV1 { __typename emojis " - "}\nfragment FriendsFeedItemFriendSuggestionsDetails on FriendsFeedItemFriendSuggestionsV1 { " - "__typename suggestions { __typename ...FriendSuggestionDetails } }\nfragment " - "FriendSuggestionDetails on FriendSuggestion { __typename profile { __typename ...ProfileDetails " - "invitedBy(last: 3) { __typename edges { __typename node { __typename ...ProfileDetails } } } } " - "reason }\nfragment ProfileDetails on Profile { __typename id displayName profilePhotoName username " - "bio emojis { __typename emojis } friendStatus isBlocked blockedMe hashedPhoneNumber joinedAt { " - "__typename isoString } kudos { __typename emoji totalCount lastSentAt { __typename isoString } } " - "selectsVideo { __typename ...RecapVideoDetails } music { __typename ...ProfileMusicDetails } tags " - "{ __typename type text } }\nfragment RecapVideoDetails on RecapVideo { __typename id videoFilename " - "totalDuration interval media { __typename imageFilename } }\nfragment ProfileMusicDetails on " - "ProfileMusic { __typename artist artworkUrl duration songTitle songUrl }\nfragment " + "first: 3) { __typename edges { __typename cursor node { __typename ...MediaCommentDetails } } " + "totalCount } reactions { __typename ...MediaReactionDetails } user { __typename " + "...CoreProfileFragment } timestamp { __typename isoString } }\nfragment " + "FriendsFeedItemAlbumUpdatedDetails on FriendsFeedItemAlbumUpdatedV1 { __typename id title mediaIds " + "totalCount }\nfragment FriendsFeedItemBioUpdatedDetails on FriendsFeedItemBioUpdatedV1 { " + "__typename bio }\nfragment FriendsFeedItemEmojisUpdatedDetails on FriendsFeedItemEmojisUpdatedV1 { " + "__typename emojis }\nfragment FriendsFeedItemFriendSuggestionsDetails on " + "FriendsFeedItemFriendSuggestionsV1 { __typename suggestions { __typename " + "...FriendSuggestionDetails } }\nfragment FriendSuggestionDetails on FriendSuggestion { __typename " + "profile { __typename ...ViewProfileSummaryFragment invitedBy(last: 3) { __typename edges { " + "__typename cursor node { __typename ...CoreProfileFragment } } } } reason }\nfragment " + "ViewProfileSummaryFragment on Profile { __typename ...CoreProfileFragment " + "...ViewProfileSelectsFragment ...ViewProfileMusicFragment bio emojis { __typename emojis } kudos { " + "__typename emoji totalCount lastSentAt { __typename isoString } } tags { __typename type text } " + "}\nfragment CoreProfileFragment on Profile { __typename id displayName profilePhotoName username " + "friendStatus isBlocked blockedMe hashedPhoneNumber joinedAt { __typename isoString } }\nfragment " + "ViewProfileSelectsFragment on Profile { __typename selectsVideo { __typename " + "...CoreRecapVideoFragment } }\nfragment CoreRecapVideoFragment on RecapVideo { __typename id " + "videoFilename totalDuration interval }\nfragment ViewProfileMusicFragment on Profile { __typename " + "music { __typename ...ProfileMusicDetails } }\nfragment ProfileMusicDetails on ProfileMusic { " + "__typename artist artworkUrl duration songTitle songUrl }\nfragment " "FriendsFeedItemFriendRequestsDetails on FriendsFeedItemFriendRequestsV1 { __typename requests { " "__typename ...FriendRequestDetails } }\nfragment FriendRequestDetails on FriendRequest { " - "__typename profile { __typename ...ProfileDetails invitedBy(last: 3) { __typename edges { " - "__typename cursor node { __typename ...ProfileDetails } } } } }\nfragment " + "__typename profile { __typename ...ViewProfileSummaryFragment invitedBy(last: 3) { __typename " + "edges { __typename cursor node { __typename ...CoreProfileFragment } } } } }\nfragment " "FriendsFeedItemKudosUpdatedDetails on FriendsFeedItemKudosUpdatedV1 { __typename empty }\nfragment " "FriendsFeedItemMediaFeaturedDetails on FriendsFeedItemMediaFeaturedV1 { __typename media { " - "__typename ...MediaDetails } }\nfragment MediaDetails on Media { __typename id takenAt { " - "__typename isoString } takenBy { __typename ...ProfileDetails } commentsCount developsAt { " - "__typename isoString } destroyedAt { __typename isoString } deletedAt { __typename isoString } " - "partyId timeCapsuleId timezone content { __typename filtered original } submittedToTeam reactions " - "{ __typename ...MediaReactionDetails } comments(first: 3) { __typename edges { __typename node { " - "__typename ...MediaCommentDetails } } } tags(first: 3) { __typename edges { __typename node { " - "__typename ...MediaTagDetails } } totalCount } featured faceFrames { __typename xPos yPos width " - "height } }\nfragment MediaReactionDetails on MediaReaction { __typename emoji hasReacted count " - "}\nfragment MediaCommentDetails on MediaComment { __typename id author { __typename " - "...ProfileDetails } media { __typename id } createdAt { __typename isoString } deletedAt { " - "__typename isoString } text isLiked likeCount }\nfragment MediaTagDetails on MediaTag { __typename " - "frame { __typename position { __typename xPos yPos } size { __typename width height } } taggedAt { " - "__typename isoString } taggedBy { __typename id } ... on MediaContactTag { hashedPhoneNumber } ... " - "on MediaProfileTag { profile { __typename id displayName username profilePhotoName friendStatus } " - "shared } }\nfragment FriendsFeedItemMediaSharedDetails on FriendsFeedItemMediaSharedV1 { " - "__typename entries { __typename ...FriendsFeedItemMediaSharedEntryDetails } }\nfragment " - "FriendsFeedItemMediaSharedEntryDetails on FriendsFeedItemMediaSharedEntryV1 { __typename id seen " - "media { __typename ...MediaDetails } }\nfragment FriendsFeedItemMusicUpdatedDetails on " + "__typename ...CoreMediaFragment ...MediaWithReactionsFragment } }\nfragment CoreMediaFragment on " + "Media { __typename id takenAt { __typename isoString } takenBy { __typename ...CoreProfileFragment " + "} deletedAt { __typename isoString } }\nfragment MediaWithReactionsFragment on Media { __typename " + "reactions { __typename ...MediaReactionDetails } }\nfragment MediaReactionDetails on MediaReaction " + "{ __typename emoji hasReacted count }\nfragment FriendsFeedItemMediaSharedDetails on " + "FriendsFeedItemMediaSharedV1 { __typename entries { __typename " + "...FriendsFeedItemMediaSharedEntryDetails } }\nfragment FriendsFeedItemMediaSharedEntryDetails on " + "FriendsFeedItemMediaSharedEntryV1 { __typename id seen media { __typename " + "...FullMediaPreviewFragment } }\nfragment FullMediaPreviewFragment on Media { __typename " + "...CoreMediaFragment ...MediaWithMetadataFragment ...MediaWithCommentsPreviewFragment " + "...MediaWithReactionsFragment ...MediaWithTagsPreviewFragment }\nfragment " + "MediaWithMetadataFragment on Media { __typename developsAt { __typename isoString } timezone " + "content { __typename filtered original } submittedToTeam featured faceFrames { __typename xPos " + "yPos width height } }\nfragment MediaWithCommentsPreviewFragment on Media { __typename " + "commentsCount comments(first: 3) { __typename edges { __typename cursor node { __typename " + "...MediaCommentDetails } } } }\nfragment MediaCommentDetails on MediaComment { __typename id " + "author { __typename ...CoreProfileFragment } media { __typename id } createdAt { __typename " + "isoString } deletedAt { __typename isoString } text isLiked likeCount }\nfragment " + "MediaWithTagsPreviewFragment on Media { __typename tags(first: 3) { __typename edges { __typename " + "cursor node { __typename ...MediaTagDetails } } totalCount } }\nfragment MediaTagDetails on " + "MediaTag { __typename frame { __typename position { __typename xPos yPos } size { __typename width " + "height } } taggedAt { __typename isoString } taggedBy { __typename id } ... on MediaContactTag { " + "hashedPhoneNumber } ... on MediaProfileTag { profile { __typename id displayName username " + "profilePhotoName friendStatus } shared } }\nfragment FriendsFeedItemMusicUpdatedDetails on " "FriendsFeedItemMusicUpdatedV1 { __typename artist artworkUrl songTitle }\nfragment " "FriendsFeedItemProfileCompletedDetails on FriendsFeedItemProfileCompletedV1 { __typename photoName " - "selectsVideo { __typename ...RecapVideoDetails } }\nfragment " + "selectsVideo { __typename ...CoreRecapVideoFragment } }\nfragment " "FriendsFeedItemProfilePhotoUpdatedDetails on FriendsFeedItemProfilePhotoUpdatedV1 { __typename " "profilePhotoName }\nfragment FriendsFeedItemSelectsUpdatedDetails on " "FriendsFeedItemSelectsUpdatedV1 { __typename imageFilename selectsVideo { __typename " - "...RecapVideoDetails } }\nfragment FriendsFeedItemTaggedMediaSharedDetails on " - "FriendsFeedItemTaggedMediaSharedV2 { __typename sharedMedia { __typename " - "...FriendsFeedItemMediaSharedDetails } }") + "...CoreRecapVideoFragment } }\nfragment FriendsFeedItemStatusUpdatedDetails on " + "FriendsFeedItemStatusUpdatedV1 { __typename body { __typename text } }\nfragment " + "FriendsFeedItemTaggedMediaSharedDetails on FriendsFeedItemTaggedMediaSharedV2 { __typename " + "sharedMedia { __typename ...FriendsFeedItemMediaSharedDetails } }") self.last = last self.before = None From 9e667f8ac49507e48dff74b1c6c72f0968d108b2 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 11:18:36 -0500 Subject: [PATCH 32/43] Fixed getting profile music --- lapsepy/journal/journal.py | 2 +- lapsepy/journal/structures/profile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lapsepy/journal/journal.py b/lapsepy/journal/journal.py index f7681a0..f3896b4 100644 --- a/lapsepy/journal/journal.py +++ b/lapsepy/journal/journal.py @@ -275,7 +275,7 @@ def get_profile_by_id(self, user_id: str, album_limit: int = 6, friends_limit: i pd = response.get("data", {}).get("profile", {}) def generate_profile_object(profile_data: dict) -> Profile: - music = profile_data.get("music", {}) + music = profile_data.get("music") if music is not None: profile_music = ProfileMusic( artist=music.get("artist"), diff --git a/lapsepy/journal/structures/profile.py b/lapsepy/journal/structures/profile.py index b22b6f1..8e9b3b9 100644 --- a/lapsepy/journal/structures/profile.py +++ b/lapsepy/journal/structures/profile.py @@ -52,7 +52,7 @@ def from_dict(profile_data: dict) -> "Profile": pd = profile_data - music = pd.get("music", {}) + music = pd.get("music") if music is not None: profile_music = ProfileMusic( artist=music.get("artist"), From 65a6d02f87412da1e0f30abfaa0707a3d3175a20 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 11:21:24 -0500 Subject: [PATCH 33/43] Added catch if failed to get profile picture in examples/get_friends_feed_1.py --- examples/get_friends_feed/get_friends_feed_1.py | 9 +++++++-- lapsepy/journal/structures/profile.py | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/get_friends_feed/get_friends_feed_1.py b/examples/get_friends_feed/get_friends_feed_1.py index 0d51b86..667c5fa 100644 --- a/examples/get_friends_feed/get_friends_feed_1.py +++ b/examples/get_friends_feed/get_friends_feed_1.py @@ -1,5 +1,7 @@ import os +import requests.exceptions + from lapsepy.lapse import Lapse if __name__ == '__main__': @@ -15,8 +17,11 @@ profile = friend_node.profile # Get profile picture - profile.load_profile_picture(quality=100, height=None) - profile.profile_picture.save(f"./out/{profile.username}.jpg") + try: + profile.load_profile_picture(quality=100, height=None) + profile.profile_picture.save(f"./out/{profile.username}.jpg") + except requests.exceptions.HTTPError: + print(f"Failed getting profile picture for {profile.username}") # Get all images from collections for entry in friend_node.entries: diff --git a/lapsepy/journal/structures/profile.py b/lapsepy/journal/structures/profile.py index 8e9b3b9..b2c5e9c 100644 --- a/lapsepy/journal/structures/profile.py +++ b/lapsepy/journal/structures/profile.py @@ -95,6 +95,8 @@ def load_profile_picture(self, quality: int = 100, height: int | None = None) -> logger.debug(f"Getting profile image from \"{url}\"") request = requests.get(url) + request.raise_for_status() + bytes_io = io.BytesIO(request.content) image = Image.open(bytes_io) From da2f27ecba2218de0c44fab6bfe7e04fefabfe22 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 11:29:21 -0500 Subject: [PATCH 34/43] Revert "Added catch if failed to get profile picture in examples/get_friends_feed_1.py" This reverts commit 65a6d02f87412da1e0f30abfaa0707a3d3175a20. --- examples/get_friends_feed/get_friends_feed_1.py | 9 ++------- lapsepy/journal/structures/profile.py | 2 -- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/examples/get_friends_feed/get_friends_feed_1.py b/examples/get_friends_feed/get_friends_feed_1.py index 667c5fa..0d51b86 100644 --- a/examples/get_friends_feed/get_friends_feed_1.py +++ b/examples/get_friends_feed/get_friends_feed_1.py @@ -1,7 +1,5 @@ import os -import requests.exceptions - from lapsepy.lapse import Lapse if __name__ == '__main__': @@ -17,11 +15,8 @@ profile = friend_node.profile # Get profile picture - try: - profile.load_profile_picture(quality=100, height=None) - profile.profile_picture.save(f"./out/{profile.username}.jpg") - except requests.exceptions.HTTPError: - print(f"Failed getting profile picture for {profile.username}") + profile.load_profile_picture(quality=100, height=None) + profile.profile_picture.save(f"./out/{profile.username}.jpg") # Get all images from collections for entry in friend_node.entries: diff --git a/lapsepy/journal/structures/profile.py b/lapsepy/journal/structures/profile.py index b2c5e9c..8e9b3b9 100644 --- a/lapsepy/journal/structures/profile.py +++ b/lapsepy/journal/structures/profile.py @@ -95,8 +95,6 @@ def load_profile_picture(self, quality: int = 100, height: int | None = None) -> logger.debug(f"Getting profile image from \"{url}\"") request = requests.get(url) - request.raise_for_status() - bytes_io = io.BytesIO(request.content) image = Image.open(bytes_io) From e8d5441557c1e002845680abd76d92812d3aa37f Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 11:36:01 -0500 Subject: [PATCH 35/43] Fixed accidental revert of detecting failed profile picture retrieval --- examples/get_friends_feed/get_friends_feed_1.py | 9 +++++++-- lapsepy/journal/structures/profile.py | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/get_friends_feed/get_friends_feed_1.py b/examples/get_friends_feed/get_friends_feed_1.py index 0d51b86..667c5fa 100644 --- a/examples/get_friends_feed/get_friends_feed_1.py +++ b/examples/get_friends_feed/get_friends_feed_1.py @@ -1,5 +1,7 @@ import os +import requests.exceptions + from lapsepy.lapse import Lapse if __name__ == '__main__': @@ -15,8 +17,11 @@ profile = friend_node.profile # Get profile picture - profile.load_profile_picture(quality=100, height=None) - profile.profile_picture.save(f"./out/{profile.username}.jpg") + try: + profile.load_profile_picture(quality=100, height=None) + profile.profile_picture.save(f"./out/{profile.username}.jpg") + except requests.exceptions.HTTPError: + print(f"Failed getting profile picture for {profile.username}") # Get all images from collections for entry in friend_node.entries: diff --git a/lapsepy/journal/structures/profile.py b/lapsepy/journal/structures/profile.py index 8e9b3b9..46a4a09 100644 --- a/lapsepy/journal/structures/profile.py +++ b/lapsepy/journal/structures/profile.py @@ -95,6 +95,8 @@ def load_profile_picture(self, quality: int = 100, height: int | None = None) -> logger.debug(f"Getting profile image from \"{url}\"") request = requests.get(url) + request.raise_for_status() + bytes_io = io.BytesIO(request.content) image = Image.open(bytes_io) From 2311713ed435e037b6c4fd13c6f63b816c6595be Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 17:56:47 -0500 Subject: [PATCH 36/43] Added driving code --- lapsepy/journal/factory/media_factory.py | 19 +++++++++++++++++++ lapsepy/journal/journal.py | 17 ++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/lapsepy/journal/factory/media_factory.py b/lapsepy/journal/factory/media_factory.py index c62489a..69af66d 100644 --- a/lapsepy/journal/factory/media_factory.py +++ b/lapsepy/journal/factory/media_factory.py @@ -167,3 +167,22 @@ def _render_variables(self): "isoString": self.iso_string } } + + +class AddReactionGQL(BaseGQL): + def __init__(self, msg_id: str, reaction: str): + super().__init__("AddReactionGraphQLMutation", + "mutation AddReactionGraphQLMutation($input: AddMediaReactionInput!) " + "{ addMediaReaction(input: $input) { __typename success } }") + self.msg_id = msg_id + self.reaction = reaction + + self.variables = {} + + self._render_variables() + + def _render_variables(self): + self.variables['input'] = { + "id": self.msg_id, + "reaction": self.reaction + } \ No newline at end of file diff --git a/lapsepy/journal/journal.py b/lapsepy/journal/journal.py index f3896b4..7c94905 100644 --- a/lapsepy/journal/journal.py +++ b/lapsepy/journal/journal.py @@ -16,7 +16,7 @@ from .factory.friends_factory import FriendsFeedItemsGQL, ProfileDetailsGQL, SendKudosGQL from .factory.media_factory import ImageUploadURLGQL, CreateMediaGQL, SendInstantsGQL, StatusUpdateGQL, \ - RemoveFriendsFeedItemGQL + RemoveFriendsFeedItemGQL, AddReactionGQL from lapsepy.journal.factory.profile_factory import SaveBioGQL, SaveDisplayNameGQL, SaveUsernameGQL, SaveEmojisGQL, \ SaveDOBGQL @@ -208,6 +208,8 @@ def remove_status_update(self, msg_id: str, removed_at: datetime | None): query = RemoveFriendsFeedItemGQL(msg_id=msg_id, iso_string=removed_at).to_dict() response = self._sync_journal_call(query) + print(response) + if not response.get("data", {}).get("removeFriendsFeedItem", {}).get("success"): raise SyncJournalException("Failed removing status.") @@ -380,3 +382,16 @@ def modify_dob(self, dob: str): if not response.get('data', {}).get("saveDateOfBirth", {}).get("success"): raise SyncJournalException("Error saving date of birth.") + + def add_reaction(self, msg_id: str, reaction: str): + """ + Adds a reaction to a message + :param msg_id: ID of msg to send reaction to. + :param reaction: Reaction to send. + :return: + """ + query = AddReactionGQL(msg_id=msg_id, reaction=reaction).to_dict() + response = self._sync_journal_call(query) + + if not response.get('data', {}).get("addMediaReaction", {}).get("success"): + raise SyncJournalException("Error adding reaction.") From 38550b37c1792bc7c2002fea78ac0f45c0d2acf5 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 18:06:30 -0500 Subject: [PATCH 37/43] Added reactions --- examples/add_reaction/add_reaction_01.py | 11 +++++++++++ lapsepy/lapse/lapse.py | 9 +++++++++ 2 files changed, 20 insertions(+) create mode 100644 examples/add_reaction/add_reaction_01.py diff --git a/examples/add_reaction/add_reaction_01.py b/examples/add_reaction/add_reaction_01.py new file mode 100644 index 0000000..c88812e --- /dev/null +++ b/examples/add_reaction/add_reaction_01.py @@ -0,0 +1,11 @@ +import os +from lapsepy.lapse import Lapse + +if __name__ == '__main__': + lapse = Lapse(refresh_token=os.getenv("REFRESH_TOKEN")) + + # Develop in 15 seconds + msg_id = input("MSG ID: ") + reaction = input("Reaction: ") + # Get friend object + friend = lapse.add_reaction(msg_id=msg_id, reaction=reaction) diff --git a/lapsepy/lapse/lapse.py b/lapsepy/lapse/lapse.py index 2a69242..5083b41 100644 --- a/lapsepy/lapse/lapse.py +++ b/lapsepy/lapse/lapse.py @@ -176,3 +176,12 @@ def update_dob(self, dob: str): :return: None """ return self.journal.modify_dob(dob=dob) + + def add_reaction(self, msg_id: str, reaction: str): + """ + Adds a reaction to a message + :param msg_id: ID of msg to send reaction to. + :param reaction: Reaction to send. + :return: + """ + return self.journal.add_reaction(msg_id=msg_id, reaction=reaction) From 4c8c7d0116d1683ba0112fa2b500050d29459bb6 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 18:09:35 -0500 Subject: [PATCH 38/43] Added removing reactions --- lapsepy/journal/factory/media_factory.py | 18 ++++++++++++++++++ lapsepy/journal/journal.py | 15 ++++++++++++++- lapsepy/lapse/lapse.py | 9 +++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/lapsepy/journal/factory/media_factory.py b/lapsepy/journal/factory/media_factory.py index 69af66d..4fbd6b2 100644 --- a/lapsepy/journal/factory/media_factory.py +++ b/lapsepy/journal/factory/media_factory.py @@ -181,6 +181,24 @@ def __init__(self, msg_id: str, reaction: str): self._render_variables() + def _render_variables(self): + self.variables['input'] = { + "id": self.msg_id, + "reaction": self.reaction + } + + +class RemoveReactionGQL(BaseGQL): + def __init__(self, msg_id: str, reaction: str): + super().__init__("mutation RemoveReactionGraphQLMutation($input: RemoveMediaReactionInput!) " + "{ removeMediaReaction(input: $input) { __typename success } }") + self.msg_id = msg_id + self.reaction = reaction + + self.variables = {} + + self._render_variables() + def _render_variables(self): self.variables['input'] = { "id": self.msg_id, diff --git a/lapsepy/journal/journal.py b/lapsepy/journal/journal.py index 7c94905..9e6d6c5 100644 --- a/lapsepy/journal/journal.py +++ b/lapsepy/journal/journal.py @@ -16,7 +16,7 @@ from .factory.friends_factory import FriendsFeedItemsGQL, ProfileDetailsGQL, SendKudosGQL from .factory.media_factory import ImageUploadURLGQL, CreateMediaGQL, SendInstantsGQL, StatusUpdateGQL, \ - RemoveFriendsFeedItemGQL, AddReactionGQL + RemoveFriendsFeedItemGQL, AddReactionGQL, RemoveReactionGQL from lapsepy.journal.factory.profile_factory import SaveBioGQL, SaveDisplayNameGQL, SaveUsernameGQL, SaveEmojisGQL, \ SaveDOBGQL @@ -395,3 +395,16 @@ def add_reaction(self, msg_id: str, reaction: str): if not response.get('data', {}).get("addMediaReaction", {}).get("success"): raise SyncJournalException("Error adding reaction.") + + def remove_reaction(self, msg_id: str, reaction: str): + """ + removes a reaction from a message + :param msg_id: ID of msg to remove reaction from. + :param reaction: Reaction to remove. + :return: + """ + query = RemoveReactionGQL(msg_id=msg_id, reaction=reaction).to_dict() + response = self._sync_journal_call(query) + + if not response.get('data', {}).get("removeMediaReaction", {}).get("success"): + raise SyncJournalException("Error removing reaction.") diff --git a/lapsepy/lapse/lapse.py b/lapsepy/lapse/lapse.py index 5083b41..0fe5337 100644 --- a/lapsepy/lapse/lapse.py +++ b/lapsepy/lapse/lapse.py @@ -185,3 +185,12 @@ def add_reaction(self, msg_id: str, reaction: str): :return: """ return self.journal.add_reaction(msg_id=msg_id, reaction=reaction) + + def remove_reaction(self, msg_id: str, reaction: str): + """ + removes a reaction from a message + :param msg_id: ID of msg to remove reaction from. + :param reaction: Reaction to remove. + :return: + """ + return self.journal.remove_reaction(msg_id=msg_id, reaction=reaction) \ No newline at end of file From 8c66fec912ba3b4657dfff3793bcdc59e7abda4b Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 18:21:02 -0500 Subject: [PATCH 39/43] Fixed examples --- examples/add_reaction/add_reaction_01.py | 5 ++--- examples/add_reaction/add_reaction_02.py | 12 ++++++++++++ examples/remove_reaction/remove_reaction_01.py | 10 ++++++++++ lapsepy/journal/factory/media_factory.py | 5 +++-- 4 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 examples/add_reaction/add_reaction_02.py create mode 100644 examples/remove_reaction/remove_reaction_01.py diff --git a/examples/add_reaction/add_reaction_01.py b/examples/add_reaction/add_reaction_01.py index c88812e..08065e9 100644 --- a/examples/add_reaction/add_reaction_01.py +++ b/examples/add_reaction/add_reaction_01.py @@ -4,8 +4,7 @@ if __name__ == '__main__': lapse = Lapse(refresh_token=os.getenv("REFRESH_TOKEN")) - # Develop in 15 seconds msg_id = input("MSG ID: ") reaction = input("Reaction: ") - # Get friend object - friend = lapse.add_reaction(msg_id=msg_id, reaction=reaction) + + lapse.add_reaction(msg_id=msg_id, reaction=reaction) diff --git a/examples/add_reaction/add_reaction_02.py b/examples/add_reaction/add_reaction_02.py new file mode 100644 index 0000000..d5ff5e2 --- /dev/null +++ b/examples/add_reaction/add_reaction_02.py @@ -0,0 +1,12 @@ +import os +from lapsepy.lapse import Lapse + +if __name__ == '__main__': + lapse = Lapse(refresh_token=os.getenv("REFRESH_TOKEN")) + + count = int(input("How many: ")) + msg_id = input("MSG ID: ") + reaction = input("Reaction: ") + + for _ in range(count): + lapse.add_reaction(msg_id=msg_id, reaction=reaction) diff --git a/examples/remove_reaction/remove_reaction_01.py b/examples/remove_reaction/remove_reaction_01.py new file mode 100644 index 0000000..7287f8a --- /dev/null +++ b/examples/remove_reaction/remove_reaction_01.py @@ -0,0 +1,10 @@ +import os +from lapsepy.lapse import Lapse + +if __name__ == '__main__': + lapse = Lapse(refresh_token=os.getenv("REFRESH_TOKEN")) + + msg_id = input("MSG ID: ") + reaction = input("Reaction: ") + + lapse.remove_reaction(msg_id=msg_id, reaction=reaction) diff --git a/lapsepy/journal/factory/media_factory.py b/lapsepy/journal/factory/media_factory.py index 4fbd6b2..c0f7d44 100644 --- a/lapsepy/journal/factory/media_factory.py +++ b/lapsepy/journal/factory/media_factory.py @@ -190,7 +190,8 @@ def _render_variables(self): class RemoveReactionGQL(BaseGQL): def __init__(self, msg_id: str, reaction: str): - super().__init__("mutation RemoveReactionGraphQLMutation($input: RemoveMediaReactionInput!) " + super().__init__("RemoveReactionGraphQLMutation", + "mutation RemoveReactionGraphQLMutation($input: RemoveMediaReactionInput!) " "{ removeMediaReaction(input: $input) { __typename success } }") self.msg_id = msg_id self.reaction = reaction @@ -203,4 +204,4 @@ def _render_variables(self): self.variables['input'] = { "id": self.msg_id, "reaction": self.reaction - } \ No newline at end of file + } From 0f819dc5ef379d5461582f937b513d431a87e6fc Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 18:21:33 -0500 Subject: [PATCH 40/43] Removed print statement from remove_status_update --- lapsepy/journal/journal.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/lapsepy/journal/journal.py b/lapsepy/journal/journal.py index 9e6d6c5..da46a2a 100644 --- a/lapsepy/journal/journal.py +++ b/lapsepy/journal/journal.py @@ -208,8 +208,6 @@ def remove_status_update(self, msg_id: str, removed_at: datetime | None): query = RemoveFriendsFeedItemGQL(msg_id=msg_id, iso_string=removed_at).to_dict() response = self._sync_journal_call(query) - print(response) - if not response.get("data", {}).get("removeFriendsFeedItem", {}).get("success"): raise SyncJournalException("Failed removing status.") From 8288894ccce40a1164913b6c131d2f0fda92939b Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 20:59:10 -0500 Subject: [PATCH 41/43] Added comment gql --- lapsepy/journal/factory/media_factory.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lapsepy/journal/factory/media_factory.py b/lapsepy/journal/factory/media_factory.py index c0f7d44..b79fa18 100644 --- a/lapsepy/journal/factory/media_factory.py +++ b/lapsepy/journal/factory/media_factory.py @@ -205,3 +205,24 @@ def _render_variables(self): "id": self.msg_id, "reaction": self.reaction } + + +class SendCommentGQL(BaseGQL): + def __init__(self, comment_id: str, msg_id: str, text: str): + super().__init__("SendCommentGraphQLMutation", + "mutation SendCommentGraphQLMutation($input: SendMediaCommentInput!) " + "{ sendMediaComment(input: $input) { __typename success } }") + self.comment_id = comment_id + self.msg_id = msg_id + self.text = text + + self.variables = {} + + self._render_variables() + + def _render_variables(self): + self.variables['input'] = { + "id": self.comment_id, + "mediaId": self.msg_id, + "text": self.text + } From 03a79291fa3b37495daf7bf53e4cd95742feef34 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 21:06:08 -0500 Subject: [PATCH 42/43] Added sending comments --- examples/delete_comment/delete_comment.py | 10 ++++++++ examples/send_comment/send_comment.py | 10 ++++++++ lapsepy/journal/factory/media_factory.py | 19 ++++++++++++++ lapsepy/journal/journal.py | 31 ++++++++++++++++++++++- lapsepy/lapse/lapse.py | 21 ++++++++++++++- 5 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 examples/delete_comment/delete_comment.py create mode 100644 examples/send_comment/send_comment.py diff --git a/examples/delete_comment/delete_comment.py b/examples/delete_comment/delete_comment.py new file mode 100644 index 0000000..7055536 --- /dev/null +++ b/examples/delete_comment/delete_comment.py @@ -0,0 +1,10 @@ +import os +from lapsepy.lapse import Lapse + +if __name__ == '__main__': + lapse = Lapse(refresh_token=os.getenv("REFRESH_TOKEN")) + + msg_id = input("Message ID: ") + comment = input("Comment: ") + + lapse.delete_comment(msg_id=msg_id, comment_id=comment) diff --git a/examples/send_comment/send_comment.py b/examples/send_comment/send_comment.py new file mode 100644 index 0000000..26f9209 --- /dev/null +++ b/examples/send_comment/send_comment.py @@ -0,0 +1,10 @@ +import os +from lapsepy.lapse import Lapse + +if __name__ == '__main__': + lapse = Lapse(refresh_token=os.getenv("REFRESH_TOKEN")) + + msg_id = input("Message ID: ") + comment = input("Comment: ") + + lapse.send_comment(msg_id=msg_id, text=comment) diff --git a/lapsepy/journal/factory/media_factory.py b/lapsepy/journal/factory/media_factory.py index b79fa18..2ee7bbd 100644 --- a/lapsepy/journal/factory/media_factory.py +++ b/lapsepy/journal/factory/media_factory.py @@ -226,3 +226,22 @@ def _render_variables(self): "mediaId": self.msg_id, "text": self.text } + + +class DeleteCommentGQL(BaseGQL): + def __init__(self, comment_id: str, msg_id: str): + super().__init__("DeleteCommentGraphQLMutation", + "mutation DeleteCommentGraphQLMutation($input: DeleteMediaCommentInput!) " + "{ deleteMediaComment(input: $input) { __typename success } }") + self.comment_id = comment_id + self.msg_id = msg_id + + self.variables = {} + + self._render_variables() + + def _render_variables(self): + self.variables['input'] = { + "id": self.comment_id, + "mediaId": self.msg_id, + } diff --git a/lapsepy/journal/journal.py b/lapsepy/journal/journal.py index da46a2a..8aae69d 100644 --- a/lapsepy/journal/journal.py +++ b/lapsepy/journal/journal.py @@ -16,7 +16,7 @@ from .factory.friends_factory import FriendsFeedItemsGQL, ProfileDetailsGQL, SendKudosGQL from .factory.media_factory import ImageUploadURLGQL, CreateMediaGQL, SendInstantsGQL, StatusUpdateGQL, \ - RemoveFriendsFeedItemGQL, AddReactionGQL, RemoveReactionGQL + RemoveFriendsFeedItemGQL, AddReactionGQL, RemoveReactionGQL, SendCommentGQL, DeleteCommentGQL from lapsepy.journal.factory.profile_factory import SaveBioGQL, SaveDisplayNameGQL, SaveUsernameGQL, SaveEmojisGQL, \ SaveDOBGQL @@ -406,3 +406,32 @@ def remove_reaction(self, msg_id: str, reaction: str): if not response.get('data', {}).get("removeMediaReaction", {}).get("success"): raise SyncJournalException("Error removing reaction.") + + def create_comment(self, msg_id: str, text: str, comment_id: str | None = None): + """ + Adds a comment to a post + :param comment_id: id of the comment, leave as None unless you know what you're doing + :param msg_id: id of the message + :param text: text to send in the comment + :return: + """ + if comment_id is None: + comment_id = "01HEH" + str(uuid4()).upper().replace("-", "")[:20] + query = SendCommentGQL(comment_id=comment_id, msg_id=msg_id, text=text).to_dict() + response = self._sync_journal_call(query) + + if not response.get('data', {}).get("sendMediaComment", {}).get("success"): + raise SyncJournalException("Error sending comment.") + + def delete_comment(self, msg_id: str, comment_id: str): + """ + Deletes a comment from a lapsepy post + :param msg_id: ID of the post + :param comment_id: ID of the comment + :return: + """ + query = DeleteCommentGQL(msg_id=msg_id, comment_id=comment_id).to_dict() + response = self._sync_journal_call(query) + + if not response.get('data', {}).get("deleteMediaComment", {}).get("success"): + raise SyncJournalException("Error deleting comment.") diff --git a/lapsepy/lapse/lapse.py b/lapsepy/lapse/lapse.py index 0fe5337..11f8c7d 100644 --- a/lapsepy/lapse/lapse.py +++ b/lapsepy/lapse/lapse.py @@ -193,4 +193,23 @@ def remove_reaction(self, msg_id: str, reaction: str): :param reaction: Reaction to remove. :return: """ - return self.journal.remove_reaction(msg_id=msg_id, reaction=reaction) \ No newline at end of file + return self.journal.remove_reaction(msg_id=msg_id, reaction=reaction) + + def send_comment(self, msg_id: str, text: str, comment_id: str | None = None): + """ + Adds a comment to a post + :param comment_id: id of the comment, leave as None unless you know what you're doing + :param msg_id: id of the message + :param text: text to send in the comment + :return: + """ + return self.journal.create_comment(msg_id=msg_id, text=text, comment_id=comment_id) + + def delete_comment(self, msg_id: str, comment_id: str): + """ + Deletes a comment from a lapsepy post + :param msg_id: ID of the post + :param comment_id: ID of the comment + :return: + """ + return self.journal.delete_comment(msg_id=msg_id, comment_id=comment_id) From d7263ea2b82ff314522269c3cdb825c1df8b8282 Mon Sep 17 00:00:00 2001 From: Quintin Dunn <93884113+quintindunn@users.noreply.github.com> Date: Sun, 5 Nov 2023 21:16:55 -0500 Subject: [PATCH 43/43] Added comment structure --- lapsepy/journal/structures/comment.py | 17 +++++++++++++++++ lapsepy/journal/structures/core.py | 2 ++ 2 files changed, 19 insertions(+) create mode 100644 lapsepy/journal/structures/comment.py create mode 100644 lapsepy/journal/structures/core.py diff --git a/lapsepy/journal/structures/comment.py b/lapsepy/journal/structures/comment.py new file mode 100644 index 0000000..5965a41 --- /dev/null +++ b/lapsepy/journal/structures/comment.py @@ -0,0 +1,17 @@ +from datetime import datetime + +from .core import Media + +from .profile import Profile + + +class Comment: + def __init__(self, author: Profile, create_at: datetime, comment_id: str, likes: int, is_liked: bool, media: + Media, text: str): + self.author: Profile = author + self.created_at: create_at = create_at + self.comment_id: comment_id = comment_id + self.likes: int = likes + self.is_liked: bool = is_liked + self.media: Media = media + self.text: str = text diff --git a/lapsepy/journal/structures/core.py b/lapsepy/journal/structures/core.py new file mode 100644 index 0000000..da67200 --- /dev/null +++ b/lapsepy/journal/structures/core.py @@ -0,0 +1,2 @@ +class Media: + pass