component AP gateway: convert AP mention to XEP-0372 mentions:
when a mentions are found in AP items (either with people specified directly as target, of with `mention` tags as in https://www.w3.org/TR/activitystreams-vocabulary/#microsyntaxes), they are converted to XEP-0372 `mention` references. This is only done for Pubsub items (i.e. not for private messages). rel 369
This commit is contained in:
parent
a626b8b888
commit
2c654e017d
2 changed files with 103 additions and 16 deletions
|
|
@ -70,6 +70,7 @@ from .constants import (
|
|||
TYPE_ITEM,
|
||||
TYPE_FOLLOWERS,
|
||||
TYPE_TOMBSTONE,
|
||||
TYPE_MENTION,
|
||||
NS_AP_PUBLIC,
|
||||
PUBLIC_TUPLE
|
||||
)
|
||||
|
|
@ -89,7 +90,8 @@ PLUGIN_INFO = {
|
|||
C.PI_PROTOCOLS: [],
|
||||
C.PI_DEPENDENCIES: [
|
||||
"XEP-0060", "XEP-0084", "XEP-0106", "XEP-0277", "XEP-0292", "XEP-0329",
|
||||
"XEP-0424", "XEP-0465", "PUBSUB_CACHE", "TEXT_SYNTAXES", "IDENTITY", "XEP-0054"
|
||||
"XEP-0372", "XEP-0424", "XEP-0465", "PUBSUB_CACHE", "TEXT_SYNTAXES", "IDENTITY",
|
||||
"XEP-0054"
|
||||
],
|
||||
C.PI_RECOMMENDATIONS: [],
|
||||
C.PI_MAIN: "APGateway",
|
||||
|
|
@ -117,6 +119,7 @@ class APGateway:
|
|||
self._e = host.plugins["XEP-0106"]
|
||||
self._m = host.plugins["XEP-0277"]
|
||||
self._v = host.plugins["XEP-0292"]
|
||||
self._refs = host.plugins["XEP-0372"]
|
||||
self._r = host.plugins["XEP-0424"]
|
||||
self._pps = host.plugins["XEP-0465"]
|
||||
self._c = host.plugins["PUBSUB_CACHE"]
|
||||
|
|
@ -1694,6 +1697,8 @@ class APGateway:
|
|||
self,
|
||||
client: SatXMPPEntity,
|
||||
ap_item: dict,
|
||||
targets: Dict[str, Set[str]],
|
||||
mentions: List[Dict[str, str]],
|
||||
) -> None:
|
||||
"""We got an AP item which is a reply to an XMPP item"""
|
||||
in_reply_to = ap_item["inReplyTo"]
|
||||
|
|
@ -1741,17 +1746,22 @@ class APGateway:
|
|||
else:
|
||||
__, item_elt = await self.apItem2MbDataAndElt(ap_item)
|
||||
await self._p.publish(client, comment_service, comment_node, [item_elt])
|
||||
await self.notifyMentions(
|
||||
targets, mentions, comment_service, comment_node, item_elt["id"]
|
||||
)
|
||||
|
||||
def getAPItemTargets(self, item: Dict[str, Any]) -> Tuple[bool, Set[str], Set[str]]:
|
||||
def getAPItemTargets(
|
||||
self,
|
||||
item: Dict[str, Any]
|
||||
) -> Tuple[bool, Dict[str, Set[str]], List[Dict[str, str]]]:
|
||||
"""Retrieve targets of an AP item, and indicate if it's a public one
|
||||
|
||||
@param item: AP object payload
|
||||
@return: Are returned:
|
||||
- is_public flag, indicating if the item is world-readable
|
||||
- targets of the item
|
||||
- targets of the items
|
||||
- a dict mapping target type to targets
|
||||
"""
|
||||
targets: Set[str] = set()
|
||||
targets: Dict[str, Set[str]] = {}
|
||||
is_public = False
|
||||
# TODO: handle "audience"
|
||||
for key in ("to", "bto", "cc", "bcc"):
|
||||
|
|
@ -1768,10 +1778,35 @@ class APGateway:
|
|||
continue
|
||||
if not self.isLocalURL(value):
|
||||
continue
|
||||
targets.add(value)
|
||||
target_type = self.parseAPURL(value)[0]
|
||||
if target_type != TYPE_ACTOR:
|
||||
log.debug(f"ignoring non actor type as a target: {href}")
|
||||
else:
|
||||
targets.setdefault(target_type, set()).add(value)
|
||||
|
||||
targets_types = {self.parseAPURL(t)[0] for t in targets}
|
||||
return is_public, targets, targets_types
|
||||
mentions = []
|
||||
tags = item.get("tag")
|
||||
if tags:
|
||||
for tag in tags:
|
||||
if tag.get("type") != TYPE_MENTION:
|
||||
continue
|
||||
href = tag.get("href")
|
||||
if not href:
|
||||
log.warning('Missing "href" field from mention object: {tag!r}')
|
||||
continue
|
||||
if not self.isLocalURL(href):
|
||||
continue
|
||||
uri_type = self.parseAPURL(href)[0]
|
||||
if uri_type != TYPE_ACTOR:
|
||||
log.debug(f"ignoring non actor URI as a target: {href}")
|
||||
continue
|
||||
mention = {"uri": href}
|
||||
mentions.append(mention)
|
||||
name = tag.get("name")
|
||||
if name:
|
||||
mention["content"] = name
|
||||
|
||||
return is_public, targets, mentions
|
||||
|
||||
async def newAPItem(
|
||||
self,
|
||||
|
|
@ -1786,21 +1821,22 @@ class APGateway:
|
|||
@param node: XMPP pubsub node
|
||||
@param item: AP object payload
|
||||
"""
|
||||
is_public, targets, targets_types = self.getAPItemTargets(item)
|
||||
if not is_public and targets_types == {TYPE_ACTOR}:
|
||||
is_public, targets, mentions = self.getAPItemTargets(item)
|
||||
if not is_public and targets.keys() == {TYPE_ACTOR}:
|
||||
# this is a direct message
|
||||
await self.handleMessageAPItem(
|
||||
client, targets, destinee, item
|
||||
client, targets, mentions, destinee, item
|
||||
)
|
||||
else:
|
||||
await self.handlePubsubAPItem(
|
||||
client, targets, destinee, node, item, is_public
|
||||
client, targets, mentions, destinee, node, item, is_public
|
||||
)
|
||||
|
||||
async def handleMessageAPItem(
|
||||
self,
|
||||
client: SatXMPPEntity,
|
||||
targets: Set[str],
|
||||
targets: Dict[str, Set[str]],
|
||||
mentions: List[Dict[str, str]],
|
||||
destinee: Optional[jid.JID],
|
||||
item: dict,
|
||||
) -> None:
|
||||
|
|
@ -1810,7 +1846,11 @@ class APGateway:
|
|||
@param destinee: jid of the destinee,
|
||||
@param item: AP object payload
|
||||
"""
|
||||
targets_jids = {await self.getJIDFromId(t) for t in targets}
|
||||
targets_jids = {
|
||||
await self.getJIDFromId(t)
|
||||
for t_set in targets.values()
|
||||
for t in t_set
|
||||
}
|
||||
if destinee is not None:
|
||||
targets_jids.add(destinee)
|
||||
mb_data = await self.apItem2MBdata(item)
|
||||
|
|
@ -1826,10 +1866,53 @@ class APGateway:
|
|||
)
|
||||
await defer.DeferredList(defer_l)
|
||||
|
||||
async def notifyMentions(
|
||||
self,
|
||||
targets: Dict[str, Set[str]],
|
||||
mentions: List[Dict[str, str]],
|
||||
service: jid.JID,
|
||||
node: str,
|
||||
item_id: str,
|
||||
) -> None:
|
||||
"""Send mention notifications to recipients and mentioned entities
|
||||
|
||||
XEP-0372 (References) is used.
|
||||
|
||||
Mentions are also sent to recipients as they are primary audience (see
|
||||
https://www.w3.org/TR/activitystreams-vocabulary/#microsyntaxes).
|
||||
|
||||
"""
|
||||
anchor = uri.buildXMPPUri("pubsub", path=service.full(), node=node, item=item_id)
|
||||
seen = set()
|
||||
# we start with explicit mentions because mentions' content will be used in the
|
||||
# future to fill "begin" and "end" reference attributes (we can't do it at the
|
||||
# moment as there is no way to specify the XML element to use in the blog item).
|
||||
for mention in mentions:
|
||||
mentioned_jid = await self.getJIDFromId(mention["uri"])
|
||||
self._refs.sendReference(
|
||||
self.client,
|
||||
to_jid=mentioned_jid,
|
||||
anchor=anchor
|
||||
)
|
||||
seen.add(mentioned_jid)
|
||||
|
||||
remaining = {
|
||||
await self.getJIDFromId(t)
|
||||
for t_set in targets.values()
|
||||
for t in t_set
|
||||
} - seen
|
||||
for target in remaining:
|
||||
self._refs.sendReference(
|
||||
self.client,
|
||||
to_jid=target,
|
||||
anchor=anchor
|
||||
)
|
||||
|
||||
async def handlePubsubAPItem(
|
||||
self,
|
||||
client: SatXMPPEntity,
|
||||
targets: Set[str],
|
||||
targets: Dict[str, Set[str]],
|
||||
mentions: List[Dict[str, str]],
|
||||
destinee: Optional[jid.JID],
|
||||
node: str,
|
||||
item: dict,
|
||||
|
|
@ -1853,7 +1936,8 @@ class APGateway:
|
|||
if in_reply_to and isinstance(in_reply_to, str):
|
||||
if self.isLocalURL(in_reply_to):
|
||||
# this is a reply to an XMPP item
|
||||
return await self.newReplyToXMPPItem(client, item)
|
||||
await self.newReplyToXMPPItem(client, item, targets, mentions)
|
||||
return
|
||||
|
||||
# this item is a reply to an AP item, we use or create a corresponding node
|
||||
# for comments
|
||||
|
|
@ -1910,6 +1994,8 @@ class APGateway:
|
|||
[(subscription.subscriber, None, [item_elt])]
|
||||
)
|
||||
|
||||
await self.notifyMentions(targets, mentions, service, node, item_elt["id"])
|
||||
|
||||
async def newAPDeleteItem(
|
||||
self,
|
||||
client: SatXMPPEntity,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ TYPE_FOLLOWERS = "followers"
|
|||
TYPE_FOLLOWING = "following"
|
||||
TYPE_ITEM = "item"
|
||||
TYPE_TOMBSTONE = "Tombstone"
|
||||
TYPE_MENTION = "Mention"
|
||||
MEDIA_TYPE_AP = "application/activity+json"
|
||||
NS_AP_PUBLIC = "https://www.w3.org/ns/activitystreams#Public"
|
||||
# 3 values can be used, see https://www.w3.org/TR/activitypub/#public-addressing
|
||||
|
|
|
|||
Loading…
Reference in a new issue