diff options
Diffstat (limited to 'mastodon/streaming.py')
-rw-r--r-- | mastodon/streaming.py | 50 |
1 files changed, 28 insertions, 22 deletions
diff --git a/mastodon/streaming.py b/mastodon/streaming.py index 65acba8..2080908 100644 --- a/mastodon/streaming.py +++ b/mastodon/streaming.py | |||
@@ -1,6 +1,6 @@ | |||
1 | """ | 1 | """ |
2 | Handlers for the Streaming API: | 2 | Handlers for the Streaming API: |
3 | https://github.com/tootsuite/mastodon/blob/master/docs/Using-the-API/Streaming-API.md | 3 | https://github.com/mastodon/documentation/blob/master/content/en/methods/timelines/streaming.md |
4 | """ | 4 | """ |
5 | 5 | ||
6 | import json | 6 | import json |
@@ -14,6 +14,7 @@ from mastodon import Mastodon | |||
14 | from mastodon.Mastodon import MastodonMalformedEventError, MastodonNetworkError, MastodonReadTimeout | 14 | from mastodon.Mastodon import MastodonMalformedEventError, MastodonNetworkError, MastodonReadTimeout |
15 | from requests.exceptions import ChunkedEncodingError, ReadTimeout | 15 | from requests.exceptions import ChunkedEncodingError, ReadTimeout |
16 | 16 | ||
17 | |||
17 | class StreamListener(object): | 18 | class StreamListener(object): |
18 | """Callbacks for the streaming API. Create a subclass, override the on_xxx | 19 | """Callbacks for the streaming API. Create a subclass, override the on_xxx |
19 | methods for the kinds of events you're interested in, then pass an instance | 20 | methods for the kinds of events you're interested in, then pass an instance |
@@ -39,7 +40,7 @@ class StreamListener(object): | |||
39 | """There was a connection error, read timeout or other error fatal to | 40 | """There was a connection error, read timeout or other error fatal to |
40 | the streaming connection. The exception object about to be raised | 41 | the streaming connection. The exception object about to be raised |
41 | is passed to this function for reference. | 42 | is passed to this function for reference. |
42 | 43 | ||
43 | Note that the exception will be raised properly once you return from this | 44 | Note that the exception will be raised properly once you return from this |
44 | function, so if you are using this handler to reconnect, either never | 45 | function, so if you are using this handler to reconnect, either never |
45 | return or start a thread and then catch and ignore the exception. | 46 | return or start a thread and then catch and ignore the exception. |
@@ -55,7 +56,7 @@ class StreamListener(object): | |||
55 | contains the resulting conversation dict.""" | 56 | contains the resulting conversation dict.""" |
56 | pass | 57 | pass |
57 | 58 | ||
58 | def on_unknown_event(self, name, unknown_event = None): | 59 | def on_unknown_event(self, name, unknown_event=None): |
59 | """An unknown mastodon API event has been received. The name contains the event-name and unknown_event | 60 | """An unknown mastodon API event has been received. The name contains the event-name and unknown_event |
60 | contains the content of the unknown event. | 61 | contains the content of the unknown event. |
61 | 62 | ||
@@ -65,13 +66,12 @@ class StreamListener(object): | |||
65 | self.on_abort(exception) | 66 | self.on_abort(exception) |
66 | raise exception | 67 | raise exception |
67 | 68 | ||
68 | |||
69 | def handle_heartbeat(self): | 69 | def handle_heartbeat(self): |
70 | """The server has sent us a keep-alive message. This callback may be | 70 | """The server has sent us a keep-alive message. This callback may be |
71 | useful to carry out periodic housekeeping tasks, or just to confirm | 71 | useful to carry out periodic housekeeping tasks, or just to confirm |
72 | that the connection is still open.""" | 72 | that the connection is still open.""" |
73 | pass | 73 | pass |
74 | 74 | ||
75 | def handle_stream(self, response): | 75 | def handle_stream(self, response): |
76 | """ | 76 | """ |
77 | Handles a stream of events from the Mastodon server. When each event | 77 | Handles a stream of events from the Mastodon server. When each event |
@@ -87,7 +87,7 @@ class StreamListener(object): | |||
87 | event = {} | 87 | event = {} |
88 | line_buffer = bytearray() | 88 | line_buffer = bytearray() |
89 | try: | 89 | try: |
90 | for chunk in response.iter_content(chunk_size = 1): | 90 | for chunk in response.iter_content(chunk_size=1): |
91 | if chunk: | 91 | if chunk: |
92 | for chunk_part in chunk: | 92 | for chunk_part in chunk: |
93 | chunk_part = bytearray([chunk_part]) | 93 | chunk_part = bytearray([chunk_part]) |
@@ -95,7 +95,8 @@ class StreamListener(object): | |||
95 | try: | 95 | try: |
96 | line = line_buffer.decode('utf-8') | 96 | line = line_buffer.decode('utf-8') |
97 | except UnicodeDecodeError as err: | 97 | except UnicodeDecodeError as err: |
98 | exception = MastodonMalformedEventError("Malformed UTF-8") | 98 | exception = MastodonMalformedEventError( |
99 | "Malformed UTF-8") | ||
99 | self.on_abort(exception) | 100 | self.on_abort(exception) |
100 | six.raise_from( | 101 | six.raise_from( |
101 | exception, | 102 | exception, |
@@ -117,7 +118,8 @@ class StreamListener(object): | |||
117 | err | 118 | err |
118 | ) | 119 | ) |
119 | except MastodonReadTimeout as err: | 120 | except MastodonReadTimeout as err: |
120 | exception = MastodonReadTimeout("Timed out while reading from server."), | 121 | exception = MastodonReadTimeout( |
122 | "Timed out while reading from server."), | ||
121 | self.on_abort(exception) | 123 | self.on_abort(exception) |
122 | six.raise_from( | 124 | six.raise_from( |
123 | exception, | 125 | exception, |
@@ -141,7 +143,7 @@ class StreamListener(object): | |||
141 | else: | 143 | else: |
142 | event[key] = value | 144 | event[key] = value |
143 | return event | 145 | return event |
144 | 146 | ||
145 | def _dispatch(self, event): | 147 | def _dispatch(self, event): |
146 | try: | 148 | try: |
147 | name = event['event'] | 149 | name = event['event'] |
@@ -150,9 +152,11 @@ class StreamListener(object): | |||
150 | for_stream = json.loads(event['stream']) | 152 | for_stream = json.loads(event['stream']) |
151 | except: | 153 | except: |
152 | for_stream = None | 154 | for_stream = None |
153 | payload = json.loads(data, object_hook = Mastodon._Mastodon__json_hooks) | 155 | payload = json.loads( |
156 | data, object_hook=Mastodon._Mastodon__json_hooks) | ||
154 | except KeyError as err: | 157 | except KeyError as err: |
155 | exception = MastodonMalformedEventError('Missing field', err.args[0], event) | 158 | exception = MastodonMalformedEventError( |
159 | 'Missing field', err.args[0], event) | ||
156 | self.on_abort(exception) | 160 | self.on_abort(exception) |
157 | six.raise_from( | 161 | six.raise_from( |
158 | exception, | 162 | exception, |
@@ -170,7 +174,7 @@ class StreamListener(object): | |||
170 | # New mastodon API also supports event names with dots, | 174 | # New mastodon API also supports event names with dots, |
171 | # specifically, status_update. | 175 | # specifically, status_update. |
172 | handler_name = 'on_' + name.replace('.', '_') | 176 | handler_name = 'on_' + name.replace('.', '_') |
173 | 177 | ||
174 | # A generic way to handle unknown events to make legacy code more stable for future changes | 178 | # A generic way to handle unknown events to make legacy code more stable for future changes |
175 | handler = getattr(self, handler_name, self.on_unknown_event) | 179 | handler = getattr(self, handler_name, self.on_unknown_event) |
176 | try: | 180 | try: |
@@ -191,6 +195,7 @@ class StreamListener(object): | |||
191 | else: | 195 | else: |
192 | handler(name, payload) | 196 | handler(name, payload) |
193 | 197 | ||
198 | |||
194 | class CallbackStreamListener(StreamListener): | 199 | class CallbackStreamListener(StreamListener): |
195 | """ | 200 | """ |
196 | Simple callback stream handler class. | 201 | Simple callback stream handler class. |
@@ -198,7 +203,8 @@ class CallbackStreamListener(StreamListener): | |||
198 | Define an unknown_event_handler for new Mastodon API events. If not, the | 203 | Define an unknown_event_handler for new Mastodon API events. If not, the |
199 | listener will raise an error on new, not handled, events from the API. | 204 | listener will raise an error on new, not handled, events from the API. |
200 | """ | 205 | """ |
201 | def __init__(self, update_handler = None, local_update_handler = None, delete_handler = None, notification_handler = None, conversation_handler = None, unknown_event_handler = None, status_update_handler = None): | 206 | |
207 | def __init__(self, update_handler=None, local_update_handler=None, delete_handler=None, notification_handler=None, conversation_handler=None, unknown_event_handler=None, status_update_handler=None): | ||
202 | super(CallbackStreamListener, self).__init__() | 208 | super(CallbackStreamListener, self).__init__() |
203 | self.update_handler = update_handler | 209 | self.update_handler = update_handler |
204 | self.local_update_handler = local_update_handler | 210 | self.local_update_handler = local_update_handler |
@@ -211,29 +217,29 @@ class CallbackStreamListener(StreamListener): | |||
211 | def on_update(self, status): | 217 | def on_update(self, status): |
212 | if self.update_handler != None: | 218 | if self.update_handler != None: |
213 | self.update_handler(status) | 219 | self.update_handler(status) |
214 | 220 | ||
215 | try: | 221 | try: |
216 | if self.local_update_handler != None and not "@" in status["account"]["acct"]: | 222 | if self.local_update_handler != None and not "@" in status["account"]["acct"]: |
217 | self.local_update_handler(status) | 223 | self.local_update_handler(status) |
218 | except Exception as err: | 224 | except Exception as err: |
219 | six.raise_from( | 225 | six.raise_from( |
220 | MastodonMalformedEventError('received bad update', status), | 226 | MastodonMalformedEventError('received bad update', status), |
221 | err | 227 | err |
222 | ) | 228 | ) |
223 | 229 | ||
224 | def on_delete(self, deleted_id): | 230 | def on_delete(self, deleted_id): |
225 | if self.delete_handler != None: | 231 | if self.delete_handler != None: |
226 | self.delete_handler(deleted_id) | 232 | self.delete_handler(deleted_id) |
227 | 233 | ||
228 | def on_notification(self, notification): | 234 | def on_notification(self, notification): |
229 | if self.notification_handler != None: | 235 | if self.notification_handler != None: |
230 | self.notification_handler(notification) | 236 | self.notification_handler(notification) |
231 | 237 | ||
232 | def on_conversation(self, conversation): | 238 | def on_conversation(self, conversation): |
233 | if self.conversation_handler != None: | 239 | if self.conversation_handler != None: |
234 | self.conversation_handler(conversation) | 240 | self.conversation_handler(conversation) |
235 | 241 | ||
236 | def on_unknown_event(self, name, unknown_event = None): | 242 | def on_unknown_event(self, name, unknown_event=None): |
237 | if self.unknown_event_handler != None: | 243 | if self.unknown_event_handler != None: |
238 | self.unknown_event_handler(name, unknown_event) | 244 | self.unknown_event_handler(name, unknown_event) |
239 | else: | 245 | else: |
@@ -243,4 +249,4 @@ class CallbackStreamListener(StreamListener): | |||
243 | 249 | ||
244 | def on_status_update(self, status): | 250 | def on_status_update(self, status): |
245 | if self.status_update_handler != None: | 251 | if self.status_update_handler != None: |
246 | self.status_update_handler(status) \ No newline at end of file | 252 | self.status_update_handler(status) |