comparison sat/plugins/plugin_comp_ap_gateway/http_server.py @ 3821:0b1c30ff2cbb

component AP: XMPP identity => AP actor data converstion: XMPP identity data (coming from various XEPs, notably XEP-0292 (vCard4) and XEP-0084 (User Avatar)) are now used to complete actor data. Avatar files can now be delivered with the `avatar` type in URL. rel 368
author Goffi <goffi@goffi.org>
date Wed, 29 Jun 2022 12:06:21 +0200
parents 2032826cfbcf
children 81c79b7cafa7
comparison
equal deleted inserted replaced
3820:88e332cec47b 3821:0b1c30ff2cbb
15 15
16 # You should have received a copy of the GNU Affero General Public License 16 # You should have received a copy of the GNU Affero General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. 17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 18
19 import time 19 import time
20 import html
20 from typing import Optional, Dict, List, Any 21 from typing import Optional, Dict, List, Any
21 import json 22 import json
22 from urllib import parse 23 from urllib import parse
23 from collections import deque 24 from collections import deque
24 import unicodedata 25 import unicodedata
26 from pathlib import Path
25 from pprint import pformat 27 from pprint import pformat
26 28
27 from twisted.web import http, resource as web_resource, server 29 from twisted.web import http, resource as web_resource, server
30 from twisted.web import static
28 from twisted.python import failure 31 from twisted.python import failure
29 from twisted.internet import reactor, defer 32 from twisted.internet import reactor, defer
30 from twisted.words.protocols.jabber import jid, error 33 from twisted.words.protocols.jabber import jid, error
31 from wokkel import pubsub, rsm 34 from wokkel import pubsub, rsm
32 35
298 following = self.apg.buildAPURL(TYPE_FOLLOWING, ap_account) 301 following = self.apg.buildAPURL(TYPE_FOLLOWING, ap_account)
299 302
300 # we have to use AP account as preferredUsername because it is used to retrieve 303 # we have to use AP account as preferredUsername because it is used to retrieve
301 # actor handle (see https://socialhub.activitypub.rocks/t/how-to-retrieve-user-server-tld-handle-from-actors-url/2196) 304 # actor handle (see https://socialhub.activitypub.rocks/t/how-to-retrieve-user-server-tld-handle-from-actors-url/2196)
302 preferred_username = ap_account.split("@", 1)[0] 305 preferred_username = ap_account.split("@", 1)[0]
303 return { 306
307 identity_data = await self.apg._i.getIdentity(self.apg.client, account_jid)
308
309 actor_data = {
304 "@context": [ 310 "@context": [
305 "https://www.w3.org/ns/activitystreams", 311 "https://www.w3.org/ns/activitystreams",
306 "https://w3id.org/security/v1" 312 "https://w3id.org/security/v1"
307 ], 313 ],
308 314
320 }, 326 },
321 "endpoints": { 327 "endpoints": {
322 "sharedInbox": shared_inbox 328 "sharedInbox": shared_inbox
323 }, 329 },
324 } 330 }
331
332 if identity_data.get("nicknames"):
333 actor_data["name"] = identity_data["nicknames"][0]
334 if identity_data.get("description"):
335 # description is plain text while summary expects HTML
336 actor_data["summary"] = html.escape(identity_data["description"])
337 if identity_data.get("avatar"):
338 avatar_data = identity_data["avatar"]
339 try:
340 filename = avatar_data["filename"]
341 media_type = avatar_data["media_type"]
342 except KeyError:
343 log.error(f"incomplete avatar data: {identity_data!r}")
344 else:
345 avatar_url = self.apg.buildAPURL("avatar", filename)
346 actor_data["icon"] = {
347 "type": "Image",
348 "url": avatar_url,
349 "mediaType": media_type
350 }
351
352 return actor_data
325 353
326 def getCanonicalURL(self, request: "HTTPRequest") -> str: 354 def getCanonicalURL(self, request: "HTTPRequest") -> str:
327 return parse.urljoin( 355 return parse.urljoin(
328 f"https://{self.apg.public_url}", 356 f"https://{self.apg.public_url}",
329 request.path.decode().rstrip("/") 357 request.path.decode().rstrip("/")
616 if request_type != "shared_inbox": 644 if request_type != "shared_inbox":
617 raise exceptions.DataError(f"Invalid request type: {request_type!r}") 645 raise exceptions.DataError(f"Invalid request type: {request_type!r}")
618 ret_data = await self.APInboxRequest( 646 ret_data = await self.APInboxRequest(
619 request, None, None, None, ap_url, signing_actor 647 request, None, None, None, ap_url, signing_actor
620 ) 648 )
649 elif request_type == "avatar":
650 if len(extra_args) != 1:
651 raise exceptions.DataError("avatar argument expected in URL")
652 avatar_filename = extra_args[0]
653 avatar_path = self.apg.host.common_cache.getPath(avatar_filename)
654 return static.File(str(avatar_path)).render(request)
621 else: 655 else:
622 if len(extra_args) > 1: 656 if len(extra_args) > 1:
623 log.warning(f"unexpected extra arguments: {extra_args!r}") 657 log.warning(f"unexpected extra arguments: {extra_args!r}")
624 ap_account = extra_args[0] 658 ap_account = extra_args[0]
625 account_jid, node = await self.apg.getJIDAndNode(ap_account) 659 account_jid, node = await self.apg.getJIDAndNode(ap_account)