From efb1ebb8ce8a3a99732012ae53da89d836c712bf Mon Sep 17 00:00:00 2001 From: Lorenz Diener Date: Sat, 29 Feb 2020 18:48:01 +0100 Subject: [PATCH] Add reaction support --- CHANGELOG.rst | 1 + docs/index.rst | 46 ++++++++++++++++++++- mastodon/Mastodon.py | 95 +++++++++++++++++++++++++++++++++++--------- 3 files changed, 122 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 27a6341..b349f92 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -11,6 +11,7 @@ v1.5.1 (in progress, unreleased) * New functions: `status_bookmark`, `status_unbookmark`, `bookmarks` * New fine-grained oauth scopes: read:bookmarks and write:bookmarks. * Fixed missing notification type "poll" in push notification API and documentation.ยด +* Fixed a token loading bug * Fix header upload in account_update_credentials (Thanks gdunstone) * Commented blocklist code (Thanks marnanel for the report) * Added fallback for when magic is not available (Thanks limburgher) diff --git a/docs/index.rst b/docs/index.rst index 47016ed..6a65b7b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -791,7 +791,37 @@ Read marker dicts 'version': # A counter that is incremented whenever the marker is set to a new status 'updated_at': # The time the marker was last set, as a datetime object } - + +Announcement dicts +~~~~~~~~~~~~~~~~~~ +.. _announcement dict: + +.. code-block:: python + + mastodon.annoucements()[0] + # Returns the following dictionary: + { + 'id': # The annoucements id + 'content': # The contents of the annoucement, as an html string + 'starts_at': # The annoucements start time, as a datetime object. Can be None + 'ends_at': # The annoucements end time, as a datetime object. Can be None + 'all_day': # Boolean indicating whether the annoucement represents an "all day" event + 'published_at': # The annoucements publish time, as a datetime object + 'updated_at': # The annoucements last updated time, as a datetime object + 'read': # A boolean indicating whether the logged in user has dismissed the annoucement + 'mentions': # Users mentioned in the annoucement, as a list of mention dicts + 'tags': # Hashtags mentioned in the announcement, as a list of hashtag dicts + 'emojis': # Custom emoji used in the annoucement, as a list of emoji dicts + 'reactions': # Reactions to the annoucement, as a list of reaction dicts (documented inline here): + [ { + 'name': '# Name of the custom emoji or unicode emoji of the reaction + 'count': # Reaction counter (i.e. number of users who have added this reaction) + 'me': # True if the logged-in user has reacted with this emoji, false otherwise + 'url': # URL for the custom emoji image + 'static_url': # URL for a never-animated version of the custom emoji image + } ], + } + Admin account dicts ~~~~~~~~~~~~~~~~~~~ .. _admin account dict: @@ -1053,6 +1083,11 @@ Reading data: Preferences .. automethod:: Mastodon.preferences +Reading data: Announcements +-------------------------- + +.. automethod:: Mastodon.announcements + Writing data: Statuses ---------------------- @@ -1193,6 +1228,15 @@ for the logged-in user. .. automethod:: Mastodon.domain_block .. automethod:: Mastodon.domain_unblock + +Writing data: Announcements +--------------------------- +These functions allow you to mark annoucements read and modify reactions. + +.. automethod:: Mastodon.announcement_dismiss +.. automethod:: Mastodon.announcement_reaction_create +.. automethod:: Mastodon.announcement_reaction_delete + Pagination ---------- These functions allow for convenient retrieval of paginated data. diff --git a/mastodon/Mastodon.py b/mastodon/Mastodon.py index 5b676f3..a6ed111 100644 --- a/mastodon/Mastodon.py +++ b/mastodon/Mastodon.py @@ -216,6 +216,8 @@ class Mastodon: __DICT_VERSION_ADMIN_ACCOUNT = bigger_version("2.9.1", __DICT_VERSION_ACCOUNT) __DICT_VERSION_FEATURED_TAG = "3.0.0" __DICT_VERSION_MARKER = "3.0.0" + __DICT_VERSION_REACTION = "3.1.0" + __DICT_VERSION_ANNOUNCEMENT = bigger_version("3.1.0", __DICT_VERSION_REACTION) ### # Registering apps @@ -361,23 +363,6 @@ class Mastodon: if not self.feature_set in ["mainline", "fedibird", "pleroma"]: raise MastodonIllegalArgumentError('Requested invalid feature set') - # Versioning - if mastodon_version == None: - self.retrieve_mastodon_version() - else: - try: - self.mastodon_major, self.mastodon_minor, self.mastodon_patch = parse_version_string(mastodon_version) - except: - raise MastodonVersionError("Bad version specified") - - if not version_check_mode in ["created", "changed", "none"]: - raise MastodonIllegalArgumentError("Invalid version check method.") - self.version_check_mode = version_check_mode - - # Ratelimiting parameter check - if ratelimit_method not in ["throw", "wait", "pace"]: - raise MastodonIllegalArgumentError("Invalid ratelimit method.") - # Token loading if self.client_id is not None: if os.path.isfile(self.client_id): @@ -405,7 +390,25 @@ class Mastodon: if not (self.api_base_url is None or try_base_url == self.api_base_url): raise MastodonIllegalArgumentError('Mismatch in base URLs between files and/or specified') self.api_base_url = try_base_url - + + # Versioning + if mastodon_version == None: + self.retrieve_mastodon_version() + else: + try: + self.mastodon_major, self.mastodon_minor, self.mastodon_patch = parse_version_string(mastodon_version) + except: + raise MastodonVersionError("Bad version specified") + + if not version_check_mode in ["created", "changed", "none"]: + raise MastodonIllegalArgumentError("Invalid version check method.") + self.version_check_mode = version_check_mode + + # Ratelimiting parameter check + if ratelimit_method not in ["throw", "wait", "pace"]: + raise MastodonIllegalArgumentError("Invalid ratelimit method.") + + def retrieve_mastodon_version(self): """ Determine installed mastodon version and set major, minor and patch (not including RC info) accordingly. @@ -1593,6 +1596,20 @@ class Mastodon: """ return self.__api_request('GET', '/api/v1/preferences') + ## + # Reading data: Announcements + ## + + #/api/v1/announcements + @api_version("3.1.0", "3.1.0", __DICT_VERSION_ANNOUNCEMENT) + def announcements(self): + """ + Fetch currently active annoucements. + + Returns a list of `annoucement dicts`_. + """ + return self.__api_request('GET', '/api/v1/announcements') + ## # Reading data: Read markers ## @@ -2643,7 +2660,47 @@ class Mastodon: Remove the current push subscription the logged-in user has for this app. """ self.__api_request('DELETE', '/api/v1/push/subscription') + + ### + # Writing data: Annoucements + ### + @api_version("3.1.0", "3.1.0", "3.1.0") + def announcement_dismiss(self, id): + """ + Set the given annoucement to read. + """ + id = self.__unpack_id(id) + + url = '/api/v1/announcements/{0}/dismiss'.format(str(id)) + self.__api_request('POST', url) + + @api_version("3.1.0", "3.1.0", "3.1.0") + def announcement_reaction_create(self, id, reaction): + """ + Add a reaction to an announcement. `reaction` can either be a unicode emoji + or the name of one of the instances custom emoji. + + Will throw an API error if the reaction name is not one of the allowed things + or when trying to add a reaction that the user has already added (adding a + reaction that a different user added is legal and increments the count). + """ + id = self.__unpack_id(id) + + url = '/api/v1/announcements/{0}/reactions/{1}'.format(str(id), reaction) + self.__api_request('PUT', url) + @api_version("3.1.0", "3.1.0", "3.1.0") + def announcement_reaction_delete(self, id, reaction): + """ + Remove a reaction to an announcement. + + Will throw an API error if the reaction does not exist. + """ + id = self.__unpack_id(id) + + url = '/api/v1/announcements/{0}/reactions/{1}'.format(str(id), reaction) + self.__api_request('DELETE', url) + ### # Moderation API ### @@ -3151,7 +3208,7 @@ class Mastodon: """ Parse dates in certain known json fields, if possible. """ - known_date_fields = ["created_at", "week", "day", "expires_at", "scheduled_at", "updated_at", "last_status_at"] + known_date_fields = ["created_at", "week", "day", "expires_at", "scheduled_at", "updated_at", "last_status_at", "starts_at", "ends_at", "published_at"] for k, v in json_object.items(): if k in known_date_fields: if v != None: