Mercurial > libervia-backend
comparison libervia/backend/plugins/plugin_comp_ap_gateway/pubsub_service.py @ 4270:0d7bb4df2343
Reformatted code base using black.
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 19 Jun 2024 18:44:57 +0200 |
parents | 49019947cc76 |
children |
comparison
equal
deleted
inserted
replaced
4269:64a85ce8be70 | 4270:0d7bb4df2343 |
---|---|
35 from libervia.backend.tools import image | 35 from libervia.backend.tools import image |
36 from libervia.backend.tools.utils import ensure_deferred | 36 from libervia.backend.tools.utils import ensure_deferred |
37 from libervia.backend.tools.web import download_file | 37 from libervia.backend.tools.web import download_file |
38 from libervia.backend.memory.sqla_mapping import PubsubSub, SubscriptionState | 38 from libervia.backend.memory.sqla_mapping import PubsubSub, SubscriptionState |
39 | 39 |
40 from .constants import ( | 40 from .constants import TYPE_ACTOR, ST_AVATAR, MAX_AVATAR_SIZE |
41 TYPE_ACTOR, | |
42 ST_AVATAR, | |
43 MAX_AVATAR_SIZE | |
44 ) | |
45 | 41 |
46 | 42 |
47 log = getLogger(__name__) | 43 log = getLogger(__name__) |
48 | 44 |
49 # all nodes have the same config | 45 # all nodes have the same config |
50 NODE_CONFIG = [ | 46 NODE_CONFIG = [ |
51 {"var": "pubsub#persist_items", "type": "boolean", "value": True}, | 47 {"var": "pubsub#persist_items", "type": "boolean", "value": True}, |
52 {"var": "pubsub#max_items", "value": "max"}, | 48 {"var": "pubsub#max_items", "value": "max"}, |
53 {"var": "pubsub#access_model", "type": "list-single", "value": "open"}, | 49 {"var": "pubsub#access_model", "type": "list-single", "value": "open"}, |
54 {"var": "pubsub#publish_model", "type": "list-single", "value": "open"}, | 50 {"var": "pubsub#publish_model", "type": "list-single", "value": "open"}, |
55 | |
56 ] | 51 ] |
57 | 52 |
58 NODE_CONFIG_VALUES = {c["var"]: c["value"] for c in NODE_CONFIG} | 53 NODE_CONFIG_VALUES = {c["var"]: c["value"] for c in NODE_CONFIG} |
59 NODE_OPTIONS = {c["var"]: {} for c in NODE_CONFIG} | 54 NODE_OPTIONS = {c["var"]: {} for c in NODE_CONFIG} |
60 for c in NODE_CONFIG: | 55 for c in NODE_CONFIG: |
61 NODE_OPTIONS[c["var"]].update({k:v for k,v in c.items() if k not in ("var", "value")}) | 56 NODE_OPTIONS[c["var"]].update( |
57 {k: v for k, v in c.items() if k not in ("var", "value")} | |
58 ) | |
62 | 59 |
63 | 60 |
64 class APPubsubService(rsm.PubSubService): | 61 class APPubsubService(rsm.PubSubService): |
65 """Pubsub service for XMPP requests""" | 62 """Pubsub service for XMPP requests""" |
66 | 63 |
86 @return: requestor actor ID, recipient actor ID and recipient inbox | 83 @return: requestor actor ID, recipient actor ID and recipient inbox |
87 @raise error.StanzaError: "item-not-found" is raised if not user part is specified | 84 @raise error.StanzaError: "item-not-found" is raised if not user part is specified |
88 in requestor | 85 in requestor |
89 """ | 86 """ |
90 if not recipient.user: | 87 if not recipient.user: |
91 raise error.StanzaError( | 88 raise error.StanzaError("item-not-found", text="No user part specified") |
92 "item-not-found", | |
93 text="No user part specified" | |
94 ) | |
95 requestor_actor_id = self.apg.build_apurl(TYPE_ACTOR, requestor.userhost()) | 89 requestor_actor_id = self.apg.build_apurl(TYPE_ACTOR, requestor.userhost()) |
96 recipient_account = self.apg._e.unescape(recipient.user) | 90 recipient_account = self.apg._e.unescape(recipient.user) |
97 recipient_actor_id = await self.apg.get_ap_actor_id_from_account(recipient_account) | 91 recipient_actor_id = await self.apg.get_ap_actor_id_from_account( |
92 recipient_account | |
93 ) | |
98 inbox = await self.apg.get_ap_inbox_from_id(recipient_actor_id, use_shared=False) | 94 inbox = await self.apg.get_ap_inbox_from_id(recipient_actor_id, use_shared=False) |
99 return requestor_actor_id, recipient_actor_id, inbox | 95 return requestor_actor_id, recipient_actor_id, inbox |
100 | |
101 | 96 |
102 @ensure_deferred | 97 @ensure_deferred |
103 async def publish(self, requestor, service, nodeIdentifier, items): | 98 async def publish(self, requestor, service, nodeIdentifier, items): |
104 if self.apg.local_only and not self.apg.is_local(requestor): | 99 if self.apg.local_only and not self.apg.is_local(requestor): |
105 raise error.StanzaError( | 100 raise error.StanzaError( |
106 "forbidden", | 101 "forbidden", "Only local users can publish on this gateway." |
107 "Only local users can publish on this gateway." | |
108 ) | 102 ) |
109 if not service.user: | 103 if not service.user: |
110 raise error.StanzaError( | 104 raise error.StanzaError( |
111 "bad-request", | 105 "bad-request", |
112 "You must specify an ActivityPub actor account in JID user part." | 106 "You must specify an ActivityPub actor account in JID user part.", |
113 ) | 107 ) |
114 ap_account = self.apg._e.unescape(service.user) | 108 ap_account = self.apg._e.unescape(service.user) |
115 if ap_account.count("@") != 1: | 109 if ap_account.count("@") != 1: |
116 raise error.StanzaError( | 110 raise error.StanzaError( |
117 "bad-request", | 111 "bad-request", f"{ap_account!r} is not a valid ActivityPub actor account." |
118 f"{ap_account!r} is not a valid ActivityPub actor account." | |
119 ) | 112 ) |
120 | 113 |
121 client = self.apg.client.get_virtual_client(requestor) | 114 client = self.apg.client.get_virtual_client(requestor) |
122 if self.apg._pa.is_attachment_node(nodeIdentifier): | 115 if self.apg._pa.is_attachment_node(nodeIdentifier): |
123 await self.apg.convert_and_post_attachments( | 116 await self.apg.convert_and_post_attachments( |
128 client, ap_account, service, nodeIdentifier, items | 121 client, ap_account, service, nodeIdentifier, items |
129 ) | 122 ) |
130 cached_node = await self.host.memory.storage.get_pubsub_node( | 123 cached_node = await self.host.memory.storage.get_pubsub_node( |
131 client, service, nodeIdentifier, with_subscriptions=True, create=True | 124 client, service, nodeIdentifier, with_subscriptions=True, create=True |
132 ) | 125 ) |
133 await self.host.memory.storage.cache_pubsub_items( | 126 await self.host.memory.storage.cache_pubsub_items(client, cached_node, items) |
134 client, | |
135 cached_node, | |
136 items | |
137 ) | |
138 for subscription in cached_node.subscriptions: | 127 for subscription in cached_node.subscriptions: |
139 if subscription.state != SubscriptionState.SUBSCRIBED: | 128 if subscription.state != SubscriptionState.SUBSCRIBED: |
140 continue | 129 continue |
141 self.notifyPublish( | 130 self.notifyPublish( |
142 service, | 131 service, nodeIdentifier, [(subscription.subscriber, None, items)] |
143 nodeIdentifier, | 132 ) |
144 [(subscription.subscriber, None, items)] | 133 |
145 ) | 134 async def ap_following_2_elt( |
146 | 135 self, requestor_actor_id: str, ap_item: dict |
147 async def ap_following_2_elt(self, requestor_actor_id: str, ap_item: dict) -> domish.Element: | 136 ) -> domish.Element: |
148 """Convert actor ID from following collection to XMPP item | 137 """Convert actor ID from following collection to XMPP item |
149 | 138 |
150 @param requestor_actor_id: ID of the actor doing the request. | 139 @param requestor_actor_id: ID of the actor doing the request. |
151 @param ap_item: AP item from which actor ID must be extracted. | 140 @param ap_item: AP item from which actor ID must be extracted. |
152 """ | 141 """ |
157 ) | 146 ) |
158 item_elt = pubsub.Item(id=actor_id, payload=subscription_elt) | 147 item_elt = pubsub.Item(id=actor_id, payload=subscription_elt) |
159 return item_elt | 148 return item_elt |
160 | 149 |
161 async def ap_follower_2_elt( | 150 async def ap_follower_2_elt( |
162 self, | 151 self, requestor_actor_id: str, ap_item: dict |
163 requestor_actor_id: str, | |
164 ap_item: dict | |
165 ) -> domish.Element: | 152 ) -> domish.Element: |
166 """Convert actor ID from followers collection to XMPP item | 153 """Convert actor ID from followers collection to XMPP item |
167 | 154 |
168 @param requestor_actor_id: ID of the actor doing the request. | 155 @param requestor_actor_id: ID of the actor doing the request. |
169 @param ap_item: AP item from which actor ID must be extracted. | 156 @param ap_item: AP item from which actor ID must be extracted. |
173 subscriber_elt = self.apg._pps.build_subscriber_elt(actor_jid) | 160 subscriber_elt = self.apg._pps.build_subscriber_elt(actor_jid) |
174 item_elt = pubsub.Item(id=actor_id, payload=subscriber_elt) | 161 item_elt = pubsub.Item(id=actor_id, payload=subscriber_elt) |
175 return item_elt | 162 return item_elt |
176 | 163 |
177 async def generate_v_card( | 164 async def generate_v_card( |
178 self, | 165 self, requestor_actor_id: str, ap_account: str |
179 requestor_actor_id: str, | |
180 ap_account: str | |
181 ) -> domish.Element: | 166 ) -> domish.Element: |
182 """Generate vCard4 (XEP-0292) item element from ap_account's metadata | 167 """Generate vCard4 (XEP-0292) item element from ap_account's metadata |
183 | 168 |
184 @param requestor_actor_id: ID of the actor doing the request. | 169 @param requestor_actor_id: ID of the actor doing the request. |
185 @param ap_account: AP account from where the vcard must be retrieved. | 170 @param ap_account: AP account from where the vcard must be retrieved. |
186 @return: <item> with the <vcard> element | 171 @return: <item> with the <vcard> element |
187 """ | 172 """ |
188 actor_data = await self.apg.get_ap_actor_data_from_account( | 173 actor_data = await self.apg.get_ap_actor_data_from_account( |
189 requestor_actor_id, | 174 requestor_actor_id, ap_account |
190 ap_account | |
191 ) | 175 ) |
192 identity_data = {} | 176 identity_data = {} |
193 | 177 |
194 summary = actor_data.get("summary") | 178 summary = actor_data.get("summary") |
195 # summary is HTML, we have to convert it to text | 179 # summary is HTML, we have to convert it to text |
210 item_elt.addChild(vcard_elt) | 194 item_elt.addChild(vcard_elt) |
211 item_elt["id"] = self.apg._p.ID_SINGLETON | 195 item_elt["id"] = self.apg._p.ID_SINGLETON |
212 return item_elt | 196 return item_elt |
213 | 197 |
214 async def get_avatar_data( | 198 async def get_avatar_data( |
215 self, | 199 self, client: SatXMPPEntity, requestor_actor_id: str, ap_account: str |
216 client: SatXMPPEntity, | |
217 requestor_actor_id: str, | |
218 ap_account: str | |
219 ) -> dict[str, Any]: | 200 ) -> dict[str, Any]: |
220 """Retrieve actor's avatar if any, cache it and file actor_data | 201 """Retrieve actor's avatar if any, cache it and file actor_data |
221 | 202 |
222 @param client: client to use for the request. | 203 @param client: client to use for the request. |
223 @param requestor_actor_id: ID of the actor doing the request. | 204 @param requestor_actor_id: ID of the actor doing the request. |
257 dest_path = Path(dir_name, filename) | 238 dest_path = Path(dir_name, filename) |
258 await download_file(url, dest_path, max_size=MAX_AVATAR_SIZE) | 239 await download_file(url, dest_path, max_size=MAX_AVATAR_SIZE) |
259 avatar_data = { | 240 avatar_data = { |
260 "path": dest_path, | 241 "path": dest_path, |
261 "filename": filename, | 242 "filename": filename, |
262 'media_type': image.guess_type(dest_path), | 243 "media_type": image.guess_type(dest_path), |
263 } | 244 } |
264 | 245 |
265 await self.apg._i.cache_avatar( | 246 await self.apg._i.cache_avatar(self.apg.IMPORT_NAME, avatar_data) |
266 self.apg.IMPORT_NAME, | |
267 avatar_data | |
268 ) | |
269 else: | 247 else: |
270 avatar_data = { | 248 avatar_data = { |
271 "cache_uid": cache["uid"], | 249 "cache_uid": cache["uid"], |
272 "path": cache["path"], | 250 "path": cache["path"], |
273 "media_type": cache["mime_type"] | 251 "media_type": cache["mime_type"], |
274 } | 252 } |
275 | 253 |
276 return avatar_data | 254 return avatar_data |
277 | 255 |
278 async def generate_avatar_metadata( | 256 async def generate_avatar_metadata( |
279 self, | 257 self, client: SatXMPPEntity, requestor_actor_id: str, ap_account: str |
280 client: SatXMPPEntity, | |
281 requestor_actor_id: str, | |
282 ap_account: str | |
283 ) -> domish.Element: | 258 ) -> domish.Element: |
284 """Generate the metadata element for user avatar | 259 """Generate the metadata element for user avatar |
285 | 260 |
286 @param requestor_actor_id: ID of the actor doing the request. | 261 @param requestor_actor_id: ID of the actor doing the request. |
287 @raise StanzaError("item-not-found"): no avatar is present in actor data (in | 262 @raise StanzaError("item-not-found"): no avatar is present in actor data (in |
306 @param requestor_actor_id: ID of the actor doing the request. | 281 @param requestor_actor_id: ID of the actor doing the request. |
307 @raise StanzaError("item-not-found"): no avatar cached with requested ID | 282 @raise StanzaError("item-not-found"): no avatar cached with requested ID |
308 """ | 283 """ |
309 if not itemIdentifiers: | 284 if not itemIdentifiers: |
310 avatar_data = await self.get_avatar_data( | 285 avatar_data = await self.get_avatar_data( |
311 client, | 286 client, requestor_actor_id, ap_account |
312 requestor_actor_id, | |
313 ap_account | |
314 ) | 287 ) |
315 if "base64" not in avatar_data: | 288 if "base64" not in avatar_data: |
316 await threads.deferToThread( | 289 await threads.deferToThread( |
317 self._blocking_b_6_4_encode_avatar, | 290 self._blocking_b_6_4_encode_avatar, avatar_data |
318 avatar_data | |
319 ) | 291 ) |
320 else: | 292 else: |
321 if len(itemIdentifiers) > 1: | 293 if len(itemIdentifiers) > 1: |
322 # only a single item ID is supported | 294 # only a single item ID is supported |
323 raise error.StanzaError("item-not-found") | 295 raise error.StanzaError("item-not-found") |
325 # just to be sure that that we don't have an empty string | 297 # just to be sure that that we don't have an empty string |
326 assert item_id | 298 assert item_id |
327 cache_data = self.apg.host.common_cache.get_metadata(item_id) | 299 cache_data = self.apg.host.common_cache.get_metadata(item_id) |
328 if cache_data is None: | 300 if cache_data is None: |
329 raise error.StanzaError("item-not-found") | 301 raise error.StanzaError("item-not-found") |
330 avatar_data = { | 302 avatar_data = {"cache_uid": item_id, "path": cache_data["path"]} |
331 "cache_uid": item_id, | |
332 "path": cache_data["path"] | |
333 } | |
334 await threads.deferToThread(self._blocking_b_6_4_encode_avatar, avatar_data) | 303 await threads.deferToThread(self._blocking_b_6_4_encode_avatar, avatar_data) |
335 | 304 |
336 return self.apg._a.build_item_data_elt(avatar_data) | 305 return self.apg._a.build_item_data_elt(avatar_data) |
337 | 306 |
338 @ensure_deferred | 307 @ensure_deferred |
341 requestor: jid.JID, | 310 requestor: jid.JID, |
342 service: jid.JID, | 311 service: jid.JID, |
343 node: str, | 312 node: str, |
344 maxItems: Optional[int], | 313 maxItems: Optional[int], |
345 itemIdentifiers: Optional[List[str]], | 314 itemIdentifiers: Optional[List[str]], |
346 rsm_req: Optional[rsm.RSMRequest] | 315 rsm_req: Optional[rsm.RSMRequest], |
347 ) -> Tuple[List[domish.Element], Optional[rsm.RSMResponse]]: | 316 ) -> Tuple[List[domish.Element], Optional[rsm.RSMResponse]]: |
348 if not service.user: | 317 if not service.user: |
349 return [], None | 318 return [], None |
350 ap_account = self.host.plugins["XEP-0106"].unescape(service.user) | 319 ap_account = self.host.plugins["XEP-0106"].unescape(service.user) |
351 if ap_account.count("@") != 1: | 320 if ap_account.count("@") != 1: |
352 log.warning(f"Invalid AP account used by {requestor}: {ap_account!r}") | 321 log.warning(f"Invalid AP account used by {requestor}: {ap_account!r}") |
353 return [], None | 322 return [], None |
354 | 323 |
355 requestor_actor_id = self.apg.build_apurl( | 324 requestor_actor_id = self.apg.build_apurl( |
356 TYPE_ACTOR, | 325 TYPE_ACTOR, await self.apg.get_ap_account_from_jid_and_node(service, node) |
357 await self.apg.get_ap_account_from_jid_and_node(service, node) | |
358 ) | 326 ) |
359 | 327 |
360 # cached_node may be pre-filled with some nodes (e.g. attachments nodes), | 328 # cached_node may be pre-filled with some nodes (e.g. attachments nodes), |
361 # otherwise it is filled when suitable | 329 # otherwise it is filled when suitable |
362 cached_node = None | 330 cached_node = None |
377 # vCard4 request | 345 # vCard4 request |
378 item_elt = await self.generate_v_card(requestor_actor_id, ap_account) | 346 item_elt = await self.generate_v_card(requestor_actor_id, ap_account) |
379 return [item_elt], None | 347 return [item_elt], None |
380 elif node == self.apg._a.namespace_metadata: | 348 elif node == self.apg._a.namespace_metadata: |
381 item_elt = await self.generate_avatar_metadata( | 349 item_elt = await self.generate_avatar_metadata( |
382 self.apg.client, | 350 self.apg.client, requestor_actor_id, ap_account |
383 requestor_actor_id, | |
384 ap_account | |
385 ) | 351 ) |
386 return [item_elt], None | 352 return [item_elt], None |
387 elif node == self.apg._a.namespace_data: | 353 elif node == self.apg._a.namespace_data: |
388 item_elt = await self.generate_avatar_data( | 354 item_elt = await self.generate_avatar_data( |
389 self.apg.client, | 355 self.apg.client, requestor_actor_id, ap_account, itemIdentifiers |
390 requestor_actor_id, | |
391 ap_account, | |
392 itemIdentifiers | |
393 ) | 356 ) |
394 return [item_elt], None | 357 return [item_elt], None |
395 elif self.apg._pa.is_attachment_node(node): | 358 elif self.apg._pa.is_attachment_node(node): |
396 use_cache = True | 359 use_cache = True |
397 # we check cache here because we emit an item-not-found error if the node is | 360 # we check cache here because we emit an item-not-found error if the node is |
408 parser = self.apg.ap_events.ap_item_2_event_elt | 371 parser = self.apg.ap_events.ap_item_2_event_elt |
409 else: | 372 else: |
410 raise error.StanzaError( | 373 raise error.StanzaError( |
411 "feature-not-implemented", | 374 "feature-not-implemented", |
412 text=f"AP Gateway {C.APP_VERSION} only supports " | 375 text=f"AP Gateway {C.APP_VERSION} only supports " |
413 f"{self.apg._m.namespace} node for now" | 376 f"{self.apg._m.namespace} node for now", |
414 ) | 377 ) |
415 collection_name = "outbox" | 378 collection_name = "outbox" |
416 use_cache = True | 379 use_cache = True |
417 | 380 |
418 if use_cache: | 381 if use_cache: |
442 return items, None | 405 return items, None |
443 else: | 406 else: |
444 if rsm_req is None: | 407 if rsm_req is None: |
445 if maxItems is None: | 408 if maxItems is None: |
446 maxItems = 20 | 409 maxItems = 20 |
447 kwargs.update({ | 410 kwargs.update( |
448 "max_items": maxItems, | 411 { |
449 "chronological_pagination": False, | 412 "max_items": maxItems, |
450 }) | 413 "chronological_pagination": False, |
414 } | |
415 ) | |
451 else: | 416 else: |
452 if len( | 417 if ( |
453 [v for v in (rsm_req.after, rsm_req.before, rsm_req.index) | 418 len( |
454 if v is not None] | 419 [ |
455 ) > 1: | 420 v |
421 for v in (rsm_req.after, rsm_req.before, rsm_req.index) | |
422 if v is not None | |
423 ] | |
424 ) | |
425 > 1 | |
426 ): | |
456 raise error.StanzaError( | 427 raise error.StanzaError( |
457 "bad-request", | 428 "bad-request", |
458 text="You can't use after, before and index at the same time" | 429 text="You can't use after, before and index at the same time", |
459 ) | 430 ) |
460 kwargs.update({"max_items": rsm_req.max}) | 431 kwargs.update({"max_items": rsm_req.max}) |
461 if rsm_req.after is not None: | 432 if rsm_req.after is not None: |
462 kwargs["after_id"] = rsm_req.after | 433 kwargs["after_id"] = rsm_req.after |
463 elif rsm_req.before is not None: | 434 elif rsm_req.before is not None: |
474 if self.apg._m.is_comment_node(node): | 445 if self.apg._m.is_comment_node(node): |
475 parent_item = self.apg._m.get_parent_item(node) | 446 parent_item = self.apg._m.get_parent_item(node) |
476 try: | 447 try: |
477 parent_data = await self.apg.ap_get(parent_item, requestor_actor_id) | 448 parent_data = await self.apg.ap_get(parent_item, requestor_actor_id) |
478 collection = await self.apg.ap_get_object( | 449 collection = await self.apg.ap_get_object( |
479 requestor_actor_id, | 450 requestor_actor_id, parent_data.get("object", {}), "replies" |
480 parent_data.get("object", {}), | |
481 "replies" | |
482 ) | 451 ) |
483 except Exception as e: | 452 except Exception as e: |
484 raise error.StanzaError( | 453 raise error.StanzaError("item-not-found", text=str(e)) |
485 "item-not-found", | |
486 text=str(e) | |
487 ) | |
488 else: | 454 else: |
489 actor_data = await self.apg.get_ap_actor_data_from_account( | 455 actor_data = await self.apg.get_ap_actor_data_from_account( |
490 requestor_actor_id, | 456 requestor_actor_id, ap_account |
491 ap_account | 457 ) |
492 ) | 458 collection = await self.apg.ap_get_object( |
493 collection = await self.apg.ap_get_object(requestor_actor_id, actor_data, collection_name) | 459 requestor_actor_id, actor_data, collection_name |
460 ) | |
494 if not collection: | 461 if not collection: |
495 raise error.StanzaError( | 462 raise error.StanzaError( |
496 "item-not-found", | 463 "item-not-found", |
497 text=f"No collection found for node {node!r} (account: {ap_account})" | 464 text=f"No collection found for node {node!r} (account: {ap_account})", |
498 ) | 465 ) |
499 | 466 |
500 kwargs["parser"] = parser | 467 kwargs["parser"] = parser |
501 return await self.apg.get_ap_items(requestor_actor_id, collection, **kwargs) | 468 return await self.apg.get_ap_items(requestor_actor_id, collection, **kwargs) |
502 | 469 |
526 ) | 493 ) |
527 subscription = None | 494 subscription = None |
528 else: | 495 else: |
529 try: | 496 try: |
530 subscription = next( | 497 subscription = next( |
531 s for s in node.subscriptions | 498 s |
499 for s in node.subscriptions | |
532 if s.subscriber == requestor.userhostJID() | 500 if s.subscriber == requestor.userhostJID() |
533 ) | 501 ) |
534 except StopIteration: | 502 except StopIteration: |
535 subscription = None | 503 subscription = None |
536 | 504 |
537 if subscription is None: | 505 if subscription is None: |
538 subscription = PubsubSub( | 506 subscription = PubsubSub(subscriber=requestor.userhostJID(), state=sub_state) |
539 subscriber=requestor.userhostJID(), | |
540 state=sub_state | |
541 ) | |
542 node.subscriptions.append(subscription) | 507 node.subscriptions.append(subscription) |
543 await self.host.memory.storage.add(node) | 508 await self.host.memory.storage.add(node) |
544 else: | 509 else: |
545 if subscription.state is None: | 510 if subscription.state is None: |
546 subscription.state = sub_state | 511 subscription.state = sub_state |
584 requestor, service | 549 requestor, service |
585 ) | 550 ) |
586 data = self.apg.create_activity( | 551 data = self.apg.create_activity( |
587 "Undo", | 552 "Undo", |
588 req_actor_id, | 553 req_actor_id, |
589 self.apg.create_activity( | 554 self.apg.create_activity("Follow", req_actor_id, recip_actor_id), |
590 "Follow", | |
591 req_actor_id, | |
592 recip_actor_id | |
593 ) | |
594 ) | 555 ) |
595 | 556 |
596 resp = await self.apg.sign_and_post(inbox, req_actor_id, data) | 557 resp = await self.apg.sign_and_post(inbox, req_actor_id, data) |
597 if resp.code >= 300: | 558 if resp.code >= 300: |
598 text = await resp.text() | 559 text = await resp.text() |
600 | 561 |
601 def getConfigurationOptions(self): | 562 def getConfigurationOptions(self): |
602 return NODE_OPTIONS | 563 return NODE_OPTIONS |
603 | 564 |
604 def getConfiguration( | 565 def getConfiguration( |
605 self, | 566 self, requestor: jid.JID, service: jid.JID, nodeIdentifier: str |
606 requestor: jid.JID, | |
607 service: jid.JID, | |
608 nodeIdentifier: str | |
609 ) -> defer.Deferred: | 567 ) -> defer.Deferred: |
610 return defer.succeed(NODE_CONFIG_VALUES) | 568 return defer.succeed(NODE_CONFIG_VALUES) |
611 | 569 |
612 def getNodeInfo( | 570 def getNodeInfo( |
613 self, | 571 self, |
614 requestor: jid.JID, | 572 requestor: jid.JID, |
615 service: jid.JID, | 573 service: jid.JID, |
616 nodeIdentifier: str, | 574 nodeIdentifier: str, |
617 pep: bool = False, | 575 pep: bool = False, |
618 recipient: Optional[jid.JID] = None | 576 recipient: Optional[jid.JID] = None, |
619 ) -> Optional[dict]: | 577 ) -> Optional[dict]: |
620 if not nodeIdentifier: | 578 if not nodeIdentifier: |
621 return None | 579 return None |
622 info = { | 580 info = {"type": "leaf", "meta-data": NODE_CONFIG} |
623 "type": "leaf", | |
624 "meta-data": NODE_CONFIG | |
625 } | |
626 return info | 581 return info |