Skip to content

Commit

Permalink
add status editing
Browse files Browse the repository at this point in the history
  • Loading branch information
halcy authored and halcy committed Nov 21, 2022
1 parent 6abaff5 commit f3d25fa
Show file tree
Hide file tree
Showing 5 changed files with 549 additions and 83 deletions.
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,5 @@ General improvements that would be good to do before doing another release:
* [ ] Split mastodon.py into parts in some way that makes sense, it's getting very unwieldy
* [x] Fix the CI
* [ ] Get test coverage like, real high
* [ ] Add all those streaming events??
* [x] Add all those streaming events??
* [ ] Document return values
17 changes: 17 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,19 @@ Admin account dicts
'account': # The user's account, as a standard user dict
}
Status edit dicts
~~~~~~~~~~~~~~~~~
.. _status edit dict:
.. code-block:: python
mastodonstatus_history(id)[0]
# Returns the following dictionary
{
TODO
}
App registration and user authentication
----------------------------------------
Before you can use the Mastodon API, you have to register your
Expand Down Expand Up @@ -967,6 +980,8 @@ These functions allow you to get information about single statuses.
.. automethod:: Mastodon.status_reblogged_by
.. automethod:: Mastodon.status_favourited_by
.. automethod:: Mastodon.status_card
.. automethod:: Mastodon.status_history
.. automethod:: Mastodon.status_source
Reading data: Scheduled statuses
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -1142,6 +1157,8 @@ interact with already posted statuses.
.. automethod:: Mastodon.status_bookmark
.. automethod:: Mastodon.status_unbookmark
.. automethod:: Mastodon.status_delete
.. automethod:: Mastodon.status_update
Writing data: Scheduled statuses
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
218 changes: 136 additions & 82 deletions mastodon/Mastodon.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,30 +229,25 @@ class Mastodon:
__DICT_VERSION_HASHTAG = "2.3.4"
__DICT_VERSION_EMOJI = "3.0.0"
__DICT_VERSION_RELATIONSHIP = "3.3.0"
__DICT_VERSION_NOTIFICATION = bigger_version(bigger_version(
"1.0.0", __DICT_VERSION_ACCOUNT), __DICT_VERSION_STATUS)
__DICT_VERSION_NOTIFICATION = bigger_version(bigger_version("1.0.0", __DICT_VERSION_ACCOUNT), __DICT_VERSION_STATUS)
__DICT_VERSION_CONTEXT = bigger_version("1.0.0", __DICT_VERSION_STATUS)
__DICT_VERSION_LIST = "2.1.0"
__DICT_VERSION_CARD = "3.2.0"
__DICT_VERSION_SEARCHRESULT = bigger_version(bigger_version(bigger_version(
"1.0.0", __DICT_VERSION_ACCOUNT), __DICT_VERSION_STATUS), __DICT_VERSION_HASHTAG)
__DICT_VERSION_SEARCHRESULT = bigger_version(bigger_version(bigger_version("1.0.0", __DICT_VERSION_ACCOUNT), __DICT_VERSION_STATUS), __DICT_VERSION_HASHTAG)
__DICT_VERSION_ACTIVITY = "2.1.2"
__DICT_VERSION_REPORT = "2.9.1"
__DICT_VERSION_PUSH = "2.4.0"
__DICT_VERSION_PUSH_NOTIF = "2.4.0"
__DICT_VERSION_FILTER = "2.4.3"
__DICT_VERSION_CONVERSATION = bigger_version(bigger_version(
"2.6.0", __DICT_VERSION_ACCOUNT), __DICT_VERSION_STATUS)
__DICT_VERSION_SCHEDULED_STATUS = bigger_version(
"2.7.0", __DICT_VERSION_STATUS)
__DICT_VERSION_CONVERSATION = bigger_version(bigger_version("2.6.0", __DICT_VERSION_ACCOUNT), __DICT_VERSION_STATUS)
__DICT_VERSION_SCHEDULED_STATUS = bigger_version("2.7.0", __DICT_VERSION_STATUS)
__DICT_VERSION_PREFERENCES = "2.8.0"
__DICT_VERSION_ADMIN_ACCOUNT = bigger_version(
"2.9.1", __DICT_VERSION_ACCOUNT)
__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)
__DICT_VERSION_ANNOUNCEMENT = bigger_version("3.1.0", __DICT_VERSION_REACTION)
__DICT_VERSION_STATUS_EDIT = "3.5.0"

###
# Registering apps
Expand Down Expand Up @@ -1797,80 +1792,21 @@ def bookmarks(self, max_id=None, min_id=None, since_id=None, limit=None):
###
# Writing data: Statuses
###
@api_version("1.0.0", "2.8.0", __DICT_VERSION_STATUS)
def status_post(self, status, in_reply_to_id=None, media_ids=None,
def __status_internal(self, status, in_reply_to_id=None, media_ids=None,
sensitive=False, visibility=None, spoiler_text=None,
language=None, idempotency_key=None, content_type=None,
scheduled_at=None, poll=None, quote_id=None):
"""
Post a status. Can optionally be in reply to another status and contain
media.
`media_ids` should be a list. (If it's not, the function will turn it
into one.) It can contain up to four pieces of media (uploaded via
`media_post()`_). `media_ids` can also be the `media dicts`_ returned
by `media_post()`_ - they are unpacked automatically.
The `sensitive` boolean decides whether or not media attached to the post
should be marked as sensitive, which hides it by default on the Mastodon
web front-end.
The visibility parameter is a string value and accepts any of:
'direct' - post will be visible only to mentioned users
'private' - post will be visible only to followers
'unlisted' - post will be public but not appear on the public timeline
'public' - post will be public
If not passed in, visibility defaults to match the current account's
default-privacy setting (starting with Mastodon version 1.6) or its
locked setting - private if the account is locked, public otherwise
(for Mastodon versions lower than 1.6).
The `spoiler_text` parameter is a string to be shown as a warning before
the text of the status. If no text is passed in, no warning will be
displayed.
Specify `language` to override automatic language detection. The parameter
accepts all valid ISO 639-2 language codes.
You can set `idempotency_key` to a value to uniquely identify an attempt
at posting a status. Even if you call this function more than once,
if you call it with the same `idempotency_key`, only one status will
be created.
Pass a datetime as `scheduled_at` to schedule the toot for a specific time
(the time must be at least 5 minutes into the future). If this is passed,
status_post returns a `scheduled toot dict`_ instead.
Pass `poll` to attach a poll to the status. An appropriate object can be
constructed using `make_poll()`_ . Note that as of Mastodon version
2.8.2, you can only have either media or a poll attached, not both at
the same time.
**Specific to "pleroma" feature set:**: Specify `content_type` to set
the content type of your post on Pleroma. It accepts 'text/plain' (default),
'text/markdown', 'text/html' and 'text/bbcode'. This parameter is not
supported on Mastodon servers, but will be safely ignored if set.
**Specific to "fedibird" feature set:**: The `quote_id` parameter is
a non-standard extension that specifies the id of a quoted status.
Returns a `toot dict`_ with the new status.
"""
scheduled_at=None, poll=None, quote_id=None, edit=False):
if quote_id is not None:
if self.feature_set != "fedibird":
raise MastodonIllegalArgumentError(
'quote_id is only available with feature set fedibird')
raise MastodonIllegalArgumentError('quote_id is only available with feature set fedibird')
quote_id = self.__unpack_id(quote_id)

if content_type is not None:
if self.feature_set != "pleroma":
raise MastodonIllegalArgumentError(
'content_type is only available with feature set pleroma')
raise MastodonIllegalArgumentError('content_type is only available with feature set pleroma')
# It would be better to read this from nodeinfo and cache, but this is easier
if not content_type in ["text/plain", "text/html", "text/markdown", "text/bbcode"]:
raise MastodonIllegalArgumentError(
'Invalid content type specified')
raise MastodonIllegalArgumentError('Invalid content type specified')

if in_reply_to_id is not None:
in_reply_to_id = self.__unpack_id(in_reply_to_id)
Expand All @@ -1893,8 +1829,7 @@ def status_post(self, status, in_reply_to_id=None, media_ids=None,
else:
params_initial['visibility'] = params_initial['visibility'].lower()
if params_initial['visibility'] not in valid_visibilities:
raise ValueError('Invalid visibility value! Acceptable '
'values are %s' % valid_visibilities)
raise ValueError('Invalid visibility value! Acceptable values are %s' % valid_visibilities)

if params_initial['language'] is None:
del params_initial['language']
Expand All @@ -1914,8 +1849,7 @@ def status_post(self, status, in_reply_to_id=None, media_ids=None,
for media_id in media_ids:
media_ids_proper.append(self.__unpack_id(media_id))
except Exception as e:
raise MastodonIllegalArgumentError("Invalid media "
"dict: %s" % e)
raise MastodonIllegalArgumentError("Invalid media dict: %s" % e)

params_initial["media_ids"] = media_ids_proper

Expand All @@ -1926,8 +1860,89 @@ def status_post(self, status, in_reply_to_id=None, media_ids=None,
if poll is not None:
use_json = True

params = self.__generate_params(params_initial, ['idempotency_key'])
return self.__api_request('POST', '/api/v1/statuses', params, headers=headers, use_json=use_json)
params = self.__generate_params(params_initial, ['idempotency_key', 'edit'])
if edit is None:
# Post
return self.__api_request('POST', '/api/v1/statuses', params, headers=headers, use_json=use_json)
else:
# Edit
return self.__api_request('PUT', '/api/v1/statuses/{0}'.format(str(self.__unpack_id(edit))), params, headers=headers, use_json=use_json)

@api_version("1.0.0", "2.8.0", __DICT_VERSION_STATUS)
def status_post(self, status, in_reply_to_id=None, media_ids=None,
sensitive=False, visibility=None, spoiler_text=None,
language=None, idempotency_key=None, content_type=None,
scheduled_at=None, poll=None, quote_id=None):
"""
Post a status. Can optionally be in reply to another status and contain
media.
`media_ids` should be a list. (If it's not, the function will turn it
into one.) It can contain up to four pieces of media (uploaded via
`media_post()`_). `media_ids` can also be the `media dicts`_ returned
by `media_post()`_ - they are unpacked automatically.
The `sensitive` boolean decides whether or not media attached to the post
should be marked as sensitive, which hides it by default on the Mastodon
web front-end.
The visibility parameter is a string value and accepts any of:
'direct' - post will be visible only to mentioned users
'private' - post will be visible only to followers
'unlisted' - post will be public but not appear on the public timeline
'public' - post will be public
If not passed in, visibility defaults to match the current account's
default-privacy setting (starting with Mastodon version 1.6) or its
locked setting - private if the account is locked, public otherwise
(for Mastodon versions lower than 1.6).
The `spoiler_text` parameter is a string to be shown as a warning before
the text of the status. If no text is passed in, no warning will be
displayed.
Specify `language` to override automatic language detection. The parameter
accepts all valid ISO 639-2 language codes.
You can set `idempotency_key` to a value to uniquely identify an attempt
at posting a status. Even if you call this function more than once,
if you call it with the same `idempotency_key`, only one status will
be created.
Pass a datetime as `scheduled_at` to schedule the toot for a specific time
(the time must be at least 5 minutes into the future). If this is passed,
status_post returns a `scheduled toot dict`_ instead.
Pass `poll` to attach a poll to the status. An appropriate object can be
constructed using `make_poll()`_ . Note that as of Mastodon version
2.8.2, you can only have either media or a poll attached, not both at
the same time.
**Specific to "pleroma" feature set:**: Specify `content_type` to set
the content type of your post on Pleroma. It accepts 'text/plain' (default),
'text/markdown', 'text/html' and 'text/bbcode'. This parameter is not
supported on Mastodon servers, but will be safely ignored if set.
**Specific to "fedibird" feature set:**: The `quote_id` parameter is
a non-standard extension that specifies the id of a quoted status.
Returns a `toot dict`_ with the new status.
"""
return self.__status_internal(
status,
in_reply_to_id,
media_ids,
sensitive,
visibility,
spoiler_text,
language,
idempotency_key,
content_type,
scheduled_at,
poll,
quote_id,
edit=None
)

@api_version("1.0.0", "2.8.0", __DICT_VERSION_STATUS)
def toot(self, status):
Expand All @@ -1940,6 +1955,45 @@ def toot(self, status):
"""
return self.status_post(status)

@api_version("3.5.0", "3.5.0", __DICT_VERSION_STATUS)
def status_update(self, id, status = None, spoiler_text = None, sensitive = None, media_ids = None, poll = None):
"""
Edit a status. The meanings of the fields are largely the same as in `status_post()`_,
though not every field can be edited.
Note that editing a poll will reset the votes.
"""
return self.__status_internal(
status = status,
media_ids = media_ids,
sensitive = sensitive,
spoiler_text = spoiler_text,
poll = poll,
edit = id
)

@api_version("3.5.0", "3.5.0", __DICT_VERSION_STATUS_EDIT)
def status_history(self, id):
"""
Returns the edit history of a status as a list of `status edit dicts`_, starting
from the original form. Note that this means that a status that has been edited
once will have *two* entries in this list, a status that has been edited twice
will have three, and so on.
"""
id = self.__unpack_id(id)
return self.__api_request('GET', "/api/v1/statuses/{0}/history".format(str(id)))

def status_source(self, id):
"""
Returns the source of a status for editing.
Return value is a dictionary containing exactly the parameters you could pass to
`status_update()`_ to change nothing about the status, except `status` is `text`
instead.
"""
id = self.__unpack_id(id)
return self.__api_request('GET', "/api/v1/statuses/{0}/source".format(str(id)))

@api_version("1.0.0", "2.8.0", __DICT_VERSION_STATUS)
def status_reply(self, to_status, status, in_reply_to_id=None, media_ids=None,
sensitive=False, visibility=None, spoiler_text=None,
Expand Down

0 comments on commit f3d25fa

Please sign in to comment.