Mercurial > libervia-backend
comparison sat/plugins/plugin_comp_ap_gateway/http_server.py @ 3869:c0bcbcf5b4b7
component AP gateway: handle `Like` and `Undo`/`Like` activities:
rel 370
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 21 Jul 2022 18:07:35 +0200 |
parents | aaa4e7815ba8 |
children | bd84d289fc94 |
comparison
equal
deleted
inserted
replaced
3868:37d2c0282304 | 3869:c0bcbcf5b4b7 |
---|---|
32 from wokkel import pubsub, rsm | 32 from wokkel import pubsub, rsm |
33 | 33 |
34 from sat.core import exceptions | 34 from sat.core import exceptions |
35 from sat.core.constants import Const as C | 35 from sat.core.constants import Const as C |
36 from sat.core.i18n import _ | 36 from sat.core.i18n import _ |
37 from sat.core.core_types import SatXMPPEntity | |
37 from sat.core.log import getLogger | 38 from sat.core.log import getLogger |
38 from sat.tools.common import date_utils, uri | 39 from sat.tools.common import date_utils, uri |
39 from sat.memory.sqla_mapping import SubscriptionState | 40 from sat.memory.sqla_mapping import SubscriptionState |
40 | 41 |
41 from .constants import ( | 42 from .constants import ( |
42 NS_AP, CONTENT_TYPE_AP, TYPE_ACTOR, TYPE_INBOX, TYPE_SHARED_INBOX, TYPE_OUTBOX, | 43 NS_AP, CONTENT_TYPE_AP, TYPE_ACTOR, TYPE_INBOX, TYPE_SHARED_INBOX, TYPE_OUTBOX, |
43 AP_REQUEST_TYPES, PAGE_SIZE, ACTIVITY_TYPES_LOWER, ACTIVIY_NO_ACCOUNT_ALLOWED, | 44 AP_REQUEST_TYPES, PAGE_SIZE, ACTIVITY_TYPES_LOWER, ACTIVIY_NO_ACCOUNT_ALLOWED, |
44 SIGN_HEADERS, HS2019, SIGN_EXP, TYPE_FOLLOWERS, TYPE_FOLLOWING | 45 SIGN_HEADERS, HS2019, SIGN_EXP, TYPE_FOLLOWERS, TYPE_FOLLOWING, TYPE_ITEM, TYPE_LIKE |
45 ) | 46 ) |
46 from .regex import RE_SIG_PARAM | 47 from .regex import RE_SIG_PARAM |
47 | 48 |
48 | 49 |
49 log = getLogger(__name__) | 50 log = getLogger(__name__) |
147 ) | 148 ) |
148 elif type_ == "Announce": | 149 elif type_ == "Announce": |
149 # we can use directly the Announce object, as only the "id" field is | 150 # we can use directly the Announce object, as only the "id" field is |
150 # needed | 151 # needed |
151 await self.apg.newAPDeleteItem(client, None, node, obj) | 152 await self.apg.newAPDeleteItem(client, None, node, obj) |
153 elif type_ == TYPE_LIKE: | |
154 await self.handleNewLikeItem(client, obj, True) | |
152 else: | 155 else: |
153 log.warning(f"Unmanaged undo type: {type_!r}") | 156 log.warning(f"Unmanaged undo type: {type_!r}") |
154 | 157 |
155 async def handleFollowActivity( | 158 async def handleFollowActivity( |
156 self, | 159 self, |
336 account_jid, | 339 account_jid, |
337 node, | 340 node, |
338 signing_actor, | 341 signing_actor, |
339 repeated=True | 342 repeated=True |
340 ) | 343 ) |
344 | |
345 async def handleNewLikeItem( | |
346 self, | |
347 client: SatXMPPEntity, | |
348 data: dict, | |
349 undo: bool = False, | |
350 ) -> None: | |
351 liked_ids = data.get("object") | |
352 if not liked_ids: | |
353 raise exceptions.DataError("object should be set") | |
354 elif isinstance(liked_ids, list): | |
355 try: | |
356 liked_ids = [o["id"] for o in liked_ids] | |
357 except (KeyError, TypeError): | |
358 raise exceptions.DataError(f"invalid object: {liked_ids!r}") | |
359 elif isinstance(liked_ids, dict): | |
360 obj_id = liked_ids.get("id") | |
361 if not obj_id or not isinstance(obj_id, str): | |
362 raise exceptions.DataError(f"invalid object: {liked_ids!r}") | |
363 liked_ids = [obj_id] | |
364 elif isinstance(liked_ids, str): | |
365 liked_ids = [liked_ids] | |
366 | |
367 for liked_id in liked_ids: | |
368 if not self.apg.isLocalURL(liked_id): | |
369 log.debug(f"ignoring non local liked ID: {liked_id}") | |
370 continue | |
371 url_type, url_args = self.apg.parseAPURL(liked_id) | |
372 if url_type != TYPE_ITEM: | |
373 log.warning(f"unexpected local URL for liked item: {liked_id}") | |
374 continue | |
375 try: | |
376 account, item_id = url_args | |
377 except ValueError: | |
378 raise ValueError(f"invalid URL: {liked_id}") | |
379 author_jid, item_node = await self.apg.getJIDAndNode(account) | |
380 if item_node is None: | |
381 item_node = self.apg._m.namespace | |
382 attachment_node = self.apg._pa.getAttachmentNodeName( | |
383 author_jid, item_node, item_id | |
384 ) | |
385 cached_node = await self.apg.host.memory.storage.getPubsubNode( | |
386 client, | |
387 author_jid, | |
388 attachment_node, | |
389 with_subscriptions=True, | |
390 create=True | |
391 ) | |
392 found_items, __ = await self.apg.host.memory.storage.getItems( | |
393 cached_node, item_ids=[item_id] | |
394 ) | |
395 if not found_items: | |
396 old_item_elt = None | |
397 else: | |
398 found_item = found_items[0] | |
399 old_item_elt = found_item.data | |
400 | |
401 item_elt = self.apg._pa.applySetHandler( | |
402 client, | |
403 {"extra": {"noticed": not undo}}, | |
404 old_item_elt, | |
405 [("noticed", self.apg._pa.namespace)] | |
406 ) | |
407 # we reparse the element, as there can be other attachments | |
408 attachments_data = self.apg._pa.items2attachmentData(client, [item_elt]) | |
409 # and we update the cache | |
410 await self.apg.host.memory.storage.cachePubsubItems( | |
411 client, | |
412 cached_node, | |
413 [item_elt], | |
414 attachments_data or [{}] | |
415 ) | |
416 | |
417 if self.apg.isVirtualJID(author_jid): | |
418 # the attachment is on t a virtual pubsub service (linking to an AP item), | |
419 # we notify all subscribers | |
420 for subscription in cached_node.subscriptions: | |
421 if subscription.state != SubscriptionState.SUBSCRIBED: | |
422 continue | |
423 self.apg.pubsub_service.notifyPublish( | |
424 author_jid, | |
425 attachment_node, | |
426 [(subscription.subscriber, None, [item_elt])] | |
427 ) | |
428 else: | |
429 # the attachment is on an XMPP item, we publish it to the attachment node | |
430 await self.apg._p.sendItems( | |
431 client, author_jid, attachment_node, [item_elt] | |
432 ) | |
433 | |
434 async def handleLikeActivity( | |
435 self, | |
436 request: "HTTPRequest", | |
437 data: dict, | |
438 account_jid: Optional[jid.JID], | |
439 node: Optional[str], | |
440 ap_account: Optional[str], | |
441 ap_url: str, | |
442 signing_actor: str | |
443 ): | |
444 client = await self.apg.getVirtualClient(signing_actor) | |
445 await self.handleNewLikeItem(client, data) | |
341 | 446 |
342 async def APActorRequest( | 447 async def APActorRequest( |
343 self, | 448 self, |
344 request: "HTTPRequest", | 449 request: "HTTPRequest", |
345 account_jid: jid.JID, | 450 account_jid: jid.JID, |
621 subscribers = await self.apg._pps.getPublicNodeSubscriptions( | 726 subscribers = await self.apg._pps.getPublicNodeSubscriptions( |
622 client, account_jid, node | 727 client, account_jid, node |
623 ) | 728 ) |
624 followers = [] | 729 followers = [] |
625 for subscriber in subscribers.keys(): | 730 for subscriber in subscribers.keys(): |
626 if subscriber.host == self.apg.client.jid.userhost(): | 731 if self.apg.isVirtualJID(subscriber): |
627 # the subscriber is an AP user subscribed with this gateway | 732 # the subscriber is an AP user subscribed with this gateway |
628 ap_account = self.apg._e.unescape(subscriber.user) | 733 ap_account = self.apg._e.unescape(subscriber.user) |
629 else: | 734 else: |
630 # regular XMPP user | 735 # regular XMPP user |
631 ap_account = await self.apg.getAPAccountFromJidAndNode(subscriber, node) | 736 ap_account = await self.apg.getAPAccountFromJidAndNode(subscriber, node) |
658 client, account_jid, node | 763 client, account_jid, node |
659 ) | 764 ) |
660 following = [] | 765 following = [] |
661 for sub_dict in subscriptions: | 766 for sub_dict in subscriptions: |
662 service = jid.JID(sub_dict["service"]) | 767 service = jid.JID(sub_dict["service"]) |
663 if service.host == self.apg.client.jid.userhost(): | 768 if self.apg.isVirtualJID(service): |
664 # the subscription is to an AP actor with this gateway | 769 # the subscription is to an AP actor with this gateway |
665 ap_account = self.apg._e.unescape(service.user) | 770 ap_account = self.apg._e.unescape(service.user) |
666 else: | 771 else: |
667 # regular XMPP user | 772 # regular XMPP user |
668 ap_account = await self.apg.getAPAccountFromJidAndNode( | 773 ap_account = await self.apg.getAPAccountFromJidAndNode( |