diff options
-rw-r--r-- | .circleci/config.yml | 27 | ||||
-rw-r--r-- | mastodon/Mastodon.py | 219 | ||||
-rw-r--r-- | mastodon/statuses.py | 108 | ||||
-rw-r--r-- | mastodon/timeline.py | 121 | ||||
-rw-r--r-- | tests/test_status.py | 10 |
5 files changed, 256 insertions, 229 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index e6accb3..3c677fe 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml | |||
@@ -1,5 +1,18 @@ | |||
1 | version: 2.1 | 1 | version: 2.1 |
2 | jobs: | 2 | jobs: |
3 | run-tests-36: | ||
4 | docker: | ||
5 | - image: cimg/python:3.6 | ||
6 | steps: | ||
7 | - checkout | ||
8 | - run: | ||
9 | name: "Install test deps" | ||
10 | command: "pip install .[test]" | ||
11 | - run: | ||
12 | name: "Run tests" | ||
13 | command: "python setup.py pytest --addopts '--junitxml=tests/result.xml'" | ||
14 | - store_test_results: | ||
15 | path: tests | ||
3 | run-tests-37: | 16 | run-tests-37: |
4 | docker: | 17 | docker: |
5 | - image: cimg/python:3.7 | 18 | - image: cimg/python:3.7 |
@@ -9,9 +22,6 @@ jobs: | |||
9 | name: "Install test deps" | 22 | name: "Install test deps" |
10 | command: "pip install .[test]" | 23 | command: "pip install .[test]" |
11 | - run: | 24 | - run: |
12 | name: "Install codecov" | ||
13 | command: "pip install codecov" | ||
14 | - run: | ||
15 | name: "Run tests" | 25 | name: "Run tests" |
16 | command: "python setup.py pytest --addopts '--junitxml=tests/result.xml'" | 26 | command: "python setup.py pytest --addopts '--junitxml=tests/result.xml'" |
17 | - store_test_results: | 27 | - store_test_results: |
@@ -42,10 +52,7 @@ jobs: | |||
42 | - checkout | 52 | - checkout |
43 | - run: | 53 | - run: |
44 | name: "Install test deps" | 54 | name: "Install test deps" |
45 | command: "pip install .[test]" | 55 | command: "pip install .[test]" |
46 | - run: | ||
47 | name: "Install codecov" | ||
48 | command: "pip install codecov" | ||
49 | - run: | 56 | - run: |
50 | name: "Run tests" | 57 | name: "Run tests" |
51 | command: "python setup.py pytest --addopts '--junitxml=tests/result.xml'" | 58 | command: "python setup.py pytest --addopts '--junitxml=tests/result.xml'" |
@@ -58,10 +65,7 @@ jobs: | |||
58 | - checkout | 65 | - checkout |
59 | - run: | 66 | - run: |
60 | name: "Install test deps" | 67 | name: "Install test deps" |
61 | command: "pip install .[test]" | 68 | command: "pip install .[test]" |
62 | - run: | ||
63 | name: "Install codecov" | ||
64 | command: "pip install codecov" | ||
65 | - run: | 69 | - run: |
66 | name: "Run tests" | 70 | name: "Run tests" |
67 | command: "python setup.py pytest --addopts '--junitxml=tests/result.xml'" | 71 | command: "python setup.py pytest --addopts '--junitxml=tests/result.xml'" |
@@ -70,6 +74,7 @@ jobs: | |||
70 | workflows: | 74 | workflows: |
71 | run-tests-workflow: | 75 | run-tests-workflow: |
72 | jobs: | 76 | jobs: |
77 | - run-tests-36 | ||
73 | - run-tests-37 | 78 | - run-tests-37 |
74 | - run-tests-38-cov | 79 | - run-tests-38-cov |
75 | - run-tests-39 | 80 | - run-tests-39 |
diff --git a/mastodon/Mastodon.py b/mastodon/Mastodon.py index 35b8444..0ded1cf 100644 --- a/mastodon/Mastodon.py +++ b/mastodon/Mastodon.py | |||
@@ -43,11 +43,13 @@ from .internals import Mastodon as Internals | |||
43 | from .authentication import Mastodon as Authentication | 43 | from .authentication import Mastodon as Authentication |
44 | from .accounts import Mastodon as Accounts | 44 | from .accounts import Mastodon as Accounts |
45 | from .instance import Mastodon as Instance | 45 | from .instance import Mastodon as Instance |
46 | from .timeline import Mastodon as Timeline | ||
47 | from .statuses import Mastodon as Statuses | ||
46 | 48 | ||
47 | ## | 49 | ## |
48 | # The actual Mastodon class | 50 | # The actual Mastodon class |
49 | ### | 51 | ### |
50 | class Mastodon(Utility, Authentication, Accounts, Instance): | 52 | class Mastodon(Utility, Authentication, Accounts, Instance, Timeline, Statuses): |
51 | """ | 53 | """ |
52 | Thorough and easy to use Mastodon | 54 | Thorough and easy to use Mastodon |
53 | API wrapper in Python. | 55 | API wrapper in Python. |
@@ -65,221 +67,6 @@ class Mastodon(Utility, Authentication, Accounts, Instance): | |||
65 | return Mastodon.__SUPPORTED_MASTODON_VERSION | 67 | return Mastodon.__SUPPORTED_MASTODON_VERSION |
66 | 68 | ||
67 | ### | 69 | ### |
68 | # Reading data: Timelines | ||
69 | ## | ||
70 | @api_version("1.0.0", "3.1.4", _DICT_VERSION_STATUS) | ||
71 | def timeline(self, timeline="home", max_id=None, min_id=None, since_id=None, limit=None, only_media=False, local=False, remote=False): | ||
72 | """ | ||
73 | Fetch statuses, most recent ones first. `timeline` can be 'home', 'local', 'public', | ||
74 | 'tag/hashtag' or 'list/id'. See the following functions documentation for what those do. | ||
75 | |||
76 | The default timeline is the "home" timeline. | ||
77 | |||
78 | Specify `only_media` to only get posts with attached media. Specify `local` to only get local statuses, | ||
79 | and `remote` to only get remote statuses. Some options are mutually incompatible as dictated by logic. | ||
80 | |||
81 | May or may not require authentication depending on server settings and what is specifically requested. | ||
82 | |||
83 | Returns a list of :ref:`status dicts <status dicts>`. | ||
84 | """ | ||
85 | if max_id is not None: | ||
86 | max_id = self.__unpack_id(max_id, dateconv=True) | ||
87 | |||
88 | if min_id is not None: | ||
89 | min_id = self.__unpack_id(min_id, dateconv=True) | ||
90 | |||
91 | if since_id is not None: | ||
92 | since_id = self.__unpack_id(since_id, dateconv=True) | ||
93 | |||
94 | params_initial = locals() | ||
95 | |||
96 | if not local: | ||
97 | del params_initial['local'] | ||
98 | |||
99 | if not remote: | ||
100 | del params_initial['remote'] | ||
101 | |||
102 | if not only_media: | ||
103 | del params_initial['only_media'] | ||
104 | |||
105 | if timeline == "local": | ||
106 | timeline = "public" | ||
107 | params_initial['local'] = True | ||
108 | |||
109 | params = self.__generate_params(params_initial, ['timeline']) | ||
110 | url = '/api/v1/timelines/{0}'.format(timeline) | ||
111 | return self.__api_request('GET', url, params) | ||
112 | |||
113 | @api_version("1.0.0", "3.1.4", _DICT_VERSION_STATUS) | ||
114 | def timeline_home(self, max_id=None, min_id=None, since_id=None, limit=None, only_media=False, local=False, remote=False): | ||
115 | """ | ||
116 | Convenience method: Fetches the logged-in user's home timeline (i.e. followed users and self). Params as in `timeline()`. | ||
117 | |||
118 | Returns a list of :ref:`status dicts <status dicts>`. | ||
119 | """ | ||
120 | return self.timeline('home', max_id=max_id, min_id=min_id, since_id=since_id, limit=limit, only_media=only_media, local=local, remote=remote) | ||
121 | |||
122 | @api_version("1.0.0", "3.1.4", _DICT_VERSION_STATUS) | ||
123 | def timeline_local(self, max_id=None, min_id=None, since_id=None, limit=None, only_media=False): | ||
124 | """ | ||
125 | Convenience method: Fetches the local / instance-wide timeline, not including replies. Params as in `timeline()`. | ||
126 | |||
127 | Returns a list of :ref:`status dicts <status dicts>`. | ||
128 | """ | ||
129 | return self.timeline('local', max_id=max_id, min_id=min_id, since_id=since_id, limit=limit, only_media=only_media) | ||
130 | |||
131 | @api_version("1.0.0", "3.1.4", _DICT_VERSION_STATUS) | ||
132 | def timeline_public(self, max_id=None, min_id=None, since_id=None, limit=None, only_media=False, local=False, remote=False): | ||
133 | """ | ||
134 | Convenience method: Fetches the public / visible-network / federated timeline, not including replies. Params as in `timeline()`. | ||
135 | |||
136 | Returns a list of :ref:`status dicts <status dicts>`. | ||
137 | """ | ||
138 | return self.timeline('public', max_id=max_id, min_id=min_id, since_id=since_id, limit=limit, only_media=only_media, local=local, remote=remote) | ||
139 | |||
140 | @api_version("1.0.0", "3.1.4", _DICT_VERSION_STATUS) | ||
141 | def timeline_hashtag(self, hashtag, local=False, max_id=None, min_id=None, since_id=None, limit=None, only_media=False, remote=False): | ||
142 | """ | ||
143 | Convenience method: Fetch a timeline of toots with a given hashtag. The hashtag parameter | ||
144 | should not contain the leading #. Params as in `timeline()`. | ||
145 | |||
146 | Returns a list of :ref:`status dicts <status dicts>`. | ||
147 | """ | ||
148 | if hashtag.startswith("#"): | ||
149 | raise MastodonIllegalArgumentError( | ||
150 | "Hashtag parameter should omit leading #") | ||
151 | return self.timeline('tag/{0}'.format(hashtag), max_id=max_id, min_id=min_id, since_id=since_id, limit=limit, only_media=only_media, local=local, remote=remote) | ||
152 | |||
153 | @api_version("2.1.0", "3.1.4", _DICT_VERSION_STATUS) | ||
154 | def timeline_list(self, id, max_id=None, min_id=None, since_id=None, limit=None, only_media=False, local=False, remote=False): | ||
155 | """ | ||
156 | Convenience method: Fetches a timeline containing all the toots by users in a given list. Params as in `timeline()`. | ||
157 | |||
158 | Returns a list of :ref:`status dicts <status dicts>`. | ||
159 | """ | ||
160 | id = self.__unpack_id(id) | ||
161 | return self.timeline('list/{0}'.format(id), max_id=max_id, min_id=min_id, since_id=since_id, limit=limit, only_media=only_media, local=local, remote=remote) | ||
162 | |||
163 | @api_version("2.6.0", "2.6.0", _DICT_VERSION_CONVERSATION) | ||
164 | def conversations(self, max_id=None, min_id=None, since_id=None, limit=None): | ||
165 | """ | ||
166 | Fetches a user's conversations. | ||
167 | |||
168 | Returns a list of :ref:`conversation dicts <conversation dicts>`. | ||
169 | """ | ||
170 | if max_id is not None: | ||
171 | max_id = self.__unpack_id(max_id, dateconv=True) | ||
172 | |||
173 | if min_id is not None: | ||
174 | min_id = self.__unpack_id(min_id, dateconv=True) | ||
175 | |||
176 | if since_id is not None: | ||
177 | since_id = self.__unpack_id(since_id, dateconv=True) | ||
178 | |||
179 | params = self.__generate_params(locals()) | ||
180 | return self.__api_request('GET', "/api/v1/conversations/", params) | ||
181 | |||
182 | ### | ||
183 | # Reading data: Statuses | ||
184 | ### | ||
185 | @api_version("1.0.0", "2.0.0", _DICT_VERSION_STATUS) | ||
186 | def status(self, id): | ||
187 | """ | ||
188 | Fetch information about a single toot. | ||
189 | |||
190 | Does not require authentication for publicly visible statuses. | ||
191 | |||
192 | Returns a :ref:`status dict <status dict>`. | ||
193 | """ | ||
194 | id = self.__unpack_id(id) | ||
195 | url = '/api/v1/statuses/{0}'.format(str(id)) | ||
196 | return self.__api_request('GET', url) | ||
197 | |||
198 | @api_version("1.0.0", "3.0.0", _DICT_VERSION_CARD) | ||
199 | def status_card(self, id): | ||
200 | """ | ||
201 | Fetch a card associated with a status. A card describes an object (such as an | ||
202 | external video or link) embedded into a status. | ||
203 | |||
204 | Does not require authentication for publicly visible statuses. | ||
205 | |||
206 | This function is deprecated as of 3.0.0 and the endpoint does not | ||
207 | exist anymore - you should just use the "card" field of the status dicts | ||
208 | instead. Mastodon.py will try to mimic the old behaviour, but this | ||
209 | is somewhat inefficient and not guaranteed to be the case forever. | ||
210 | |||
211 | Returns a :ref:`card dict <card dict>`. | ||
212 | """ | ||
213 | if self.verify_minimum_version("3.0.0", cached=True): | ||
214 | return self.status(id).card | ||
215 | else: | ||
216 | id = self.__unpack_id(id) | ||
217 | url = '/api/v1/statuses/{0}/card'.format(str(id)) | ||
218 | return self.__api_request('GET', url) | ||
219 | |||
220 | @api_version("1.0.0", "1.0.0", _DICT_VERSION_CONTEXT) | ||
221 | def status_context(self, id): | ||
222 | """ | ||
223 | Fetch information about ancestors and descendants of a toot. | ||
224 | |||
225 | Does not require authentication for publicly visible statuses. | ||
226 | |||
227 | Returns a :ref:`context dict <context dict>`. | ||
228 | """ | ||
229 | id = self.__unpack_id(id) | ||
230 | url = '/api/v1/statuses/{0}/context'.format(str(id)) | ||
231 | return self.__api_request('GET', url) | ||
232 | |||
233 | @api_version("1.0.0", "2.1.0", _DICT_VERSION_ACCOUNT) | ||
234 | def status_reblogged_by(self, id): | ||
235 | """ | ||
236 | Fetch a list of users that have reblogged a status. | ||
237 | |||
238 | Does not require authentication for publicly visible statuses. | ||
239 | |||
240 | Returns a list of :ref:`account dicts <account dicts>`. | ||
241 | """ | ||
242 | id = self.__unpack_id(id) | ||
243 | url = '/api/v1/statuses/{0}/reblogged_by'.format(str(id)) | ||
244 | return self.__api_request('GET', url) | ||
245 | |||
246 | @api_version("1.0.0", "2.1.0", _DICT_VERSION_ACCOUNT) | ||
247 | def status_favourited_by(self, id): | ||
248 | """ | ||
249 | Fetch a list of users that have favourited a status. | ||
250 | |||
251 | Does not require authentication for publicly visible statuses. | ||
252 | |||
253 | Returns a list of :ref:`account dicts <account dicts>`. | ||
254 | """ | ||
255 | id = self.__unpack_id(id) | ||
256 | url = '/api/v1/statuses/{0}/favourited_by'.format(str(id)) | ||
257 | return self.__api_request('GET', url) | ||
258 | |||
259 | ### | ||
260 | # Reading data: Scheduled statuses | ||
261 | ### | ||
262 | @api_version("2.7.0", "2.7.0", _DICT_VERSION_SCHEDULED_STATUS) | ||
263 | def scheduled_statuses(self): | ||
264 | """ | ||
265 | Fetch a list of scheduled statuses | ||
266 | |||
267 | Returns a list of :ref:`scheduled status dicts <scheduled status dicts>`. | ||
268 | """ | ||
269 | return self.__api_request('GET', '/api/v1/scheduled_statuses') | ||
270 | |||
271 | @api_version("2.7.0", "2.7.0", _DICT_VERSION_SCHEDULED_STATUS) | ||
272 | def scheduled_status(self, id): | ||
273 | """ | ||
274 | Fetch information about the scheduled status with the given id. | ||
275 | |||
276 | Returns a :ref:`scheduled status dict <scheduled status dict>`. | ||
277 | """ | ||
278 | id = self.__unpack_id(id) | ||
279 | url = '/api/v1/scheduled_statuses/{0}'.format(str(id)) | ||
280 | return self.__api_request('GET', url) | ||
281 | |||
282 | ### | ||
283 | # Reading data: Polls | 70 | # Reading data: Polls |
284 | ### | 71 | ### |
285 | @api_version("2.8.0", "2.8.0", _DICT_VERSION_POLL) | 72 | @api_version("2.8.0", "2.8.0", _DICT_VERSION_POLL) |
diff --git a/mastodon/statuses.py b/mastodon/statuses.py new file mode 100644 index 0000000..ae891d5 --- /dev/null +++ b/mastodon/statuses.py | |||
@@ -0,0 +1,108 @@ | |||
1 | |||
2 | from .versions import _DICT_VERSION_STATUS, _DICT_VERSION_CARD, _DICT_VERSION_CONTEXT, _DICT_VERSION_ACCOUNT, _DICT_VERSION_SCHEDULED_STATUS | ||
3 | from .utility import api_version | ||
4 | |||
5 | from .internals import Mastodon as Internals | ||
6 | |||
7 | |||
8 | class Mastodon(Internals): | ||
9 | ### | ||
10 | # Reading data: Statuses | ||
11 | ### | ||
12 | @api_version("1.0.0", "2.0.0", _DICT_VERSION_STATUS) | ||
13 | def status(self, id): | ||
14 | """ | ||
15 | Fetch information about a single toot. | ||
16 | |||
17 | Does not require authentication for publicly visible statuses. | ||
18 | |||
19 | Returns a :ref:`status dict <status dict>`. | ||
20 | """ | ||
21 | id = self.__unpack_id(id) | ||
22 | url = '/api/v1/statuses/{0}'.format(str(id)) | ||
23 | return self.__api_request('GET', url) | ||
24 | |||
25 | @api_version("1.0.0", "3.0.0", _DICT_VERSION_CARD) | ||
26 | def status_card(self, id): | ||
27 | """ | ||
28 | Fetch a card associated with a status. A card describes an object (such as an | ||
29 | external video or link) embedded into a status. | ||
30 | |||
31 | Does not require authentication for publicly visible statuses. | ||
32 | |||
33 | This function is deprecated as of 3.0.0 and the endpoint does not | ||
34 | exist anymore - you should just use the "card" field of the status dicts | ||
35 | instead. Mastodon.py will try to mimic the old behaviour, but this | ||
36 | is somewhat inefficient and not guaranteed to be the case forever. | ||
37 | |||
38 | Returns a :ref:`card dict <card dict>`. | ||
39 | """ | ||
40 | if self.verify_minimum_version("3.0.0", cached=True): | ||
41 | return self.status(id).card | ||
42 | else: | ||
43 | id = self.__unpack_id(id) | ||
44 | url = '/api/v1/statuses/{0}/card'.format(str(id)) | ||
45 | return self.__api_request('GET', url) | ||
46 | |||
47 | @api_version("1.0.0", "1.0.0", _DICT_VERSION_CONTEXT) | ||
48 | def status_context(self, id): | ||
49 | """ | ||
50 | Fetch information about ancestors and descendants of a toot. | ||
51 | |||
52 | Does not require authentication for publicly visible statuses. | ||
53 | |||
54 | Returns a :ref:`context dict <context dict>`. | ||
55 | """ | ||
56 | id = self.__unpack_id(id) | ||
57 | url = '/api/v1/statuses/{0}/context'.format(str(id)) | ||
58 | return self.__api_request('GET', url) | ||
59 | |||
60 | @api_version("1.0.0", "2.1.0", _DICT_VERSION_ACCOUNT) | ||
61 | def status_reblogged_by(self, id): | ||
62 | """ | ||
63 | Fetch a list of users that have reblogged a status. | ||
64 | |||
65 | Does not require authentication for publicly visible statuses. | ||
66 | |||
67 | Returns a list of :ref:`account dicts <account dicts>`. | ||
68 | """ | ||
69 | id = self.__unpack_id(id) | ||
70 | url = '/api/v1/statuses/{0}/reblogged_by'.format(str(id)) | ||
71 | return self.__api_request('GET', url) | ||
72 | |||
73 | @api_version("1.0.0", "2.1.0", _DICT_VERSION_ACCOUNT) | ||
74 | def status_favourited_by(self, id): | ||
75 | """ | ||
76 | Fetch a list of users that have favourited a status. | ||
77 | |||
78 | Does not require authentication for publicly visible statuses. | ||
79 | |||
80 | Returns a list of :ref:`account dicts <account dicts>`. | ||
81 | """ | ||
82 | id = self.__unpack_id(id) | ||
83 | url = '/api/v1/statuses/{0}/favourited_by'.format(str(id)) | ||
84 | return self.__api_request('GET', url) | ||
85 | |||
86 | ### | ||
87 | # Reading data: Scheduled statuses | ||
88 | ### | ||
89 | @api_version("2.7.0", "2.7.0", _DICT_VERSION_SCHEDULED_STATUS) | ||
90 | def scheduled_statuses(self): | ||
91 | """ | ||
92 | Fetch a list of scheduled statuses | ||
93 | |||
94 | Returns a list of :ref:`scheduled status dicts <scheduled status dicts>`. | ||
95 | """ | ||
96 | return self.__api_request('GET', '/api/v1/scheduled_statuses') | ||
97 | |||
98 | @api_version("2.7.0", "2.7.0", _DICT_VERSION_SCHEDULED_STATUS) | ||
99 | def scheduled_status(self, id): | ||
100 | """ | ||
101 | Fetch information about the scheduled status with the given id. | ||
102 | |||
103 | Returns a :ref:`scheduled status dict <scheduled status dict>`. | ||
104 | """ | ||
105 | id = self.__unpack_id(id) | ||
106 | url = '/api/v1/scheduled_statuses/{0}'.format(str(id)) | ||
107 | return self.__api_request('GET', url) | ||
108 | \ No newline at end of file | ||
diff --git a/mastodon/timeline.py b/mastodon/timeline.py new file mode 100644 index 0000000..b5a4068 --- /dev/null +++ b/mastodon/timeline.py | |||
@@ -0,0 +1,121 @@ | |||
1 | from .versions import _DICT_VERSION_STATUS, _DICT_VERSION_CONVERSATION | ||
2 | from .error import MastodonIllegalArgumentError, MastodonNotFoundError | ||
3 | from .utility import api_version | ||
4 | |||
5 | from .internals import Mastodon as Internals | ||
6 | |||
7 | class Mastodon(Internals): | ||
8 | ### | ||
9 | # Reading data: Timelines | ||
10 | ## | ||
11 | @api_version("1.0.0", "3.1.4", _DICT_VERSION_STATUS) | ||
12 | def timeline(self, timeline="home", max_id=None, min_id=None, since_id=None, limit=None, only_media=False, local=False, remote=False): | ||
13 | """ | ||
14 | Fetch statuses, most recent ones first. `timeline` can be 'home', 'local', 'public', | ||
15 | 'tag/hashtag' or 'list/id'. See the following functions documentation for what those do. | ||
16 | |||
17 | The default timeline is the "home" timeline. | ||
18 | |||
19 | Specify `only_media` to only get posts with attached media. Specify `local` to only get local statuses, | ||
20 | and `remote` to only get remote statuses. Some options are mutually incompatible as dictated by logic. | ||
21 | |||
22 | May or may not require authentication depending on server settings and what is specifically requested. | ||
23 | |||
24 | Returns a list of :ref:`status dicts <status dicts>`. | ||
25 | """ | ||
26 | if max_id is not None: | ||
27 | max_id = self.__unpack_id(max_id, dateconv=True) | ||
28 | |||
29 | if min_id is not None: | ||
30 | min_id = self.__unpack_id(min_id, dateconv=True) | ||
31 | |||
32 | if since_id is not None: | ||
33 | since_id = self.__unpack_id(since_id, dateconv=True) | ||
34 | |||
35 | params_initial = locals() | ||
36 | |||
37 | if not local: | ||
38 | del params_initial['local'] | ||
39 | |||
40 | if not remote: | ||
41 | del params_initial['remote'] | ||
42 | |||
43 | if not only_media: | ||
44 | del params_initial['only_media'] | ||
45 | |||
46 | if timeline == "local": | ||
47 | timeline = "public" | ||
48 | params_initial['local'] = True | ||
49 | |||
50 | params = self.__generate_params(params_initial, ['timeline']) | ||
51 | url = '/api/v1/timelines/{0}'.format(timeline) | ||
52 | return self.__api_request('GET', url, params) | ||
53 | |||
54 | @api_version("1.0.0", "3.1.4", _DICT_VERSION_STATUS) | ||
55 | def timeline_home(self, max_id=None, min_id=None, since_id=None, limit=None, only_media=False, local=False, remote=False): | ||
56 | """ | ||
57 | Convenience method: Fetches the logged-in user's home timeline (i.e. followed users and self). Params as in `timeline()`. | ||
58 | |||
59 | Returns a list of :ref:`status dicts <status dicts>`. | ||
60 | """ | ||
61 | return self.timeline('home', max_id=max_id, min_id=min_id, since_id=since_id, limit=limit, only_media=only_media, local=local, remote=remote) | ||
62 | |||
63 | @api_version("1.0.0", "3.1.4", _DICT_VERSION_STATUS) | ||
64 | def timeline_local(self, max_id=None, min_id=None, since_id=None, limit=None, only_media=False): | ||
65 | """ | ||
66 | Convenience method: Fetches the local / instance-wide timeline, not including replies. Params as in `timeline()`. | ||
67 | |||
68 | Returns a list of :ref:`status dicts <status dicts>`. | ||
69 | """ | ||
70 | return self.timeline('local', max_id=max_id, min_id=min_id, since_id=since_id, limit=limit, only_media=only_media) | ||
71 | |||
72 | @api_version("1.0.0", "3.1.4", _DICT_VERSION_STATUS) | ||
73 | def timeline_public(self, max_id=None, min_id=None, since_id=None, limit=None, only_media=False, local=False, remote=False): | ||
74 | """ | ||
75 | Convenience method: Fetches the public / visible-network / federated timeline, not including replies. Params as in `timeline()`. | ||
76 | |||
77 | Returns a list of :ref:`status dicts <status dicts>`. | ||
78 | """ | ||
79 | return self.timeline('public', max_id=max_id, min_id=min_id, since_id=since_id, limit=limit, only_media=only_media, local=local, remote=remote) | ||
80 | |||
81 | @api_version("1.0.0", "3.1.4", _DICT_VERSION_STATUS) | ||
82 | def timeline_hashtag(self, hashtag, local=False, max_id=None, min_id=None, since_id=None, limit=None, only_media=False, remote=False): | ||
83 | """ | ||
84 | Convenience method: Fetch a timeline of toots with a given hashtag. The hashtag parameter | ||
85 | should not contain the leading #. Params as in `timeline()`. | ||
86 | |||
87 | Returns a list of :ref:`status dicts <status dicts>`. | ||
88 | """ | ||
89 | if hashtag.startswith("#"): | ||
90 | raise MastodonIllegalArgumentError( | ||
91 | "Hashtag parameter should omit leading #") | ||
92 | return self.timeline('tag/{0}'.format(hashtag), max_id=max_id, min_id=min_id, since_id=since_id, limit=limit, only_media=only_media, local=local, remote=remote) | ||
93 | |||
94 | @api_version("2.1.0", "3.1.4", _DICT_VERSION_STATUS) | ||
95 | def timeline_list(self, id, max_id=None, min_id=None, since_id=None, limit=None, only_media=False, local=False, remote=False): | ||
96 | """ | ||
97 | Convenience method: Fetches a timeline containing all the toots by users in a given list. Params as in `timeline()`. | ||
98 | |||
99 | Returns a list of :ref:`status dicts <status dicts>`. | ||
100 | """ | ||
101 | id = self.__unpack_id(id) | ||
102 | return self.timeline('list/{0}'.format(id), max_id=max_id, min_id=min_id, since_id=since_id, limit=limit, only_media=only_media, local=local, remote=remote) | ||
103 | |||
104 | @api_version("2.6.0", "2.6.0", _DICT_VERSION_CONVERSATION) | ||
105 | def conversations(self, max_id=None, min_id=None, since_id=None, limit=None): | ||
106 | """ | ||
107 | Fetches a user's conversations. | ||
108 | |||
109 | Returns a list of :ref:`conversation dicts <conversation dicts>`. | ||
110 | """ | ||
111 | if max_id is not None: | ||
112 | max_id = self.__unpack_id(max_id, dateconv=True) | ||
113 | |||
114 | if min_id is not None: | ||
115 | min_id = self.__unpack_id(min_id, dateconv=True) | ||
116 | |||
117 | if since_id is not None: | ||
118 | since_id = self.__unpack_id(since_id, dateconv=True) | ||
119 | |||
120 | params = self.__generate_params(locals()) | ||
121 | return self.__api_request('GET', "/api/v1/conversations/", params) | ||
diff --git a/tests/test_status.py b/tests/test_status.py index 1fa7fd5..8461bc3 100644 --- a/tests/test_status.py +++ b/tests/test_status.py | |||
@@ -1,7 +1,13 @@ | |||
1 | import pytest | 1 | import pytest |
2 | from mastodon.Mastodon import MastodonAPIError, MastodonNotFoundError | 2 | from mastodon.Mastodon import MastodonAPIError, MastodonNotFoundError |
3 | import datetime | 3 | import datetime |
4 | import zoneinfo | 4 | try: |
5 | import zoneinfo | ||
6 | timezone = zoneinfo.ZoneInfo | ||
7 | except: | ||
8 | import pytz | ||
9 | timezone = pytz.timezone | ||
10 | |||
5 | import vcr | 11 | import vcr |
6 | import time | 12 | import time |
7 | import pickle | 13 | import pickle |
@@ -154,7 +160,7 @@ def test_status_pin_unpin(status, api): | |||
154 | 160 | ||
155 | @pytest.mark.vcr(match_on=['path']) | 161 | @pytest.mark.vcr(match_on=['path']) |
156 | def test_scheduled_status(api): | 162 | def test_scheduled_status(api): |
157 | base_time = datetime.datetime(4000, 1, 1, 12, 13, 14, 0, zoneinfo.ZoneInfo("Etc/GMT+2")) | 163 | base_time = datetime.datetime(4000, 1, 1, 12, 13, 14, 0, timezone("Etc/GMT+2")) |
158 | the_future = base_time + datetime.timedelta(minutes=20) | 164 | the_future = base_time + datetime.timedelta(minutes=20) |
159 | scheduled_toot = api.status_post("please ensure adequate headroom", scheduled_at=the_future) | 165 | scheduled_toot = api.status_post("please ensure adequate headroom", scheduled_at=the_future) |
160 | assert scheduled_toot | 166 | assert scheduled_toot |