kopia lustrzana https://github.com/snarfed/bridgy-fed
mastodon interop: webmention cleanup, tests
rodzic
4dd8f7e1a7
commit
fb977c5f52
|
@ -6,11 +6,14 @@ TODO: test error handling
|
|||
from __future__ import unicode_literals
|
||||
import copy
|
||||
import json
|
||||
import logging
|
||||
import urllib
|
||||
import urllib2
|
||||
|
||||
from django_salmon import magicsigs, utils
|
||||
import feedparser
|
||||
from granary import atom, microformats2
|
||||
import mf2py
|
||||
import mock
|
||||
from mock import call
|
||||
from oauth_dropins.webutil import util
|
||||
|
@ -19,7 +22,7 @@ import requests
|
|||
|
||||
import activitypub
|
||||
import common
|
||||
import models
|
||||
from models import MagicKey
|
||||
import testutil
|
||||
import webmention
|
||||
from webmention import app
|
||||
|
@ -37,9 +40,9 @@ class WebmentionTest(testutil.TestCase):
|
|||
<link href='http://orig/atom' rel='alternate' type='application/atom+xml'>
|
||||
</meta>
|
||||
</html>
|
||||
""", content_type='text/html; charset=utf-8')
|
||||
""", url='http://orig/post', content_type='text/html; charset=utf-8')
|
||||
|
||||
self.reply = requests_response("""\
|
||||
self.reply_html = """\
|
||||
<html>
|
||||
<body>
|
||||
<div class="h-entry">
|
||||
|
@ -50,7 +53,11 @@ class WebmentionTest(testutil.TestCase):
|
|||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""", content_type='text/html; charset=utf-8')
|
||||
"""
|
||||
self.reply = requests_response(
|
||||
self.reply_html, content_type='text/html; charset=utf-8')
|
||||
mf2 = mf2py.parse(self.reply_html, url='http://a/reply')
|
||||
self.reply_obj = microformats2.json_to_object(mf2['items'][0])
|
||||
|
||||
def test_activitypub(self, mock_get, mock_post):
|
||||
article = requests_response({
|
||||
|
@ -103,7 +110,7 @@ class WebmentionTest(testutil.TestCase):
|
|||
self.assertEqual(expected_headers, kwargs['headers'])
|
||||
|
||||
def test_salmon(self, mock_get, mock_post):
|
||||
atom = requests_response("""\
|
||||
orig_atom = requests_response("""\
|
||||
<?xml version="1.0"?>
|
||||
<entry xmlns="http://www.w3.org/2005/Atom">
|
||||
<id>tag:fed.brid.gy,2017-08-22:orig-post</id>
|
||||
|
@ -111,7 +118,7 @@ class WebmentionTest(testutil.TestCase):
|
|||
<content type="html">baz ☕ baj</content>
|
||||
</entry>
|
||||
""")
|
||||
mock_get.side_effect = [self.reply, self.orig, atom]
|
||||
mock_get.side_effect = [self.reply, self.orig, orig_atom]
|
||||
|
||||
got = app.get_response(
|
||||
'/webmention', method='POST', body=urllib.urlencode({
|
||||
|
@ -132,10 +139,14 @@ class WebmentionTest(testutil.TestCase):
|
|||
self.assertEqual(common.MAGIC_ENVELOPE_CONTENT_TYPE,
|
||||
kwargs['headers']['Content-Type'])
|
||||
|
||||
envelope = utils.parse_magic_envelope(kwargs['data'])
|
||||
assert envelope['sig']
|
||||
env = utils.parse_magic_envelope(kwargs['data'])
|
||||
self.reply_obj['inReplyTo'][0]['id'] = 'tag:fed.brid.gy,2017-08-22:orig-post'
|
||||
reply_atom = atom.activity_to_atom(
|
||||
{'object': self.reply_obj}, xml_base='http://a/reply')
|
||||
key = MagicKey.get_by_id('@a')
|
||||
assert magicsigs.verify(None, reply_atom, env['sig'], key=key)
|
||||
|
||||
data = utils.decode(envelope['data'])
|
||||
data = utils.decode(env['data'])
|
||||
parsed = feedparser.parse(data)
|
||||
entry = parsed.entries[0]
|
||||
|
||||
|
@ -155,7 +166,7 @@ class WebmentionTest(testutil.TestCase):
|
|||
entry.content[0]['value'])
|
||||
|
||||
def test_salmon_get_salmon_from_webfinger(self, mock_get, mock_post):
|
||||
atom = requests_response("""\
|
||||
orig_atom = requests_response("""\
|
||||
<?xml version="1.0"?>
|
||||
<entry xmlns="http://www.w3.org/2005/Atom">
|
||||
<author>
|
||||
|
@ -172,7 +183,7 @@ class WebmentionTest(testutil.TestCase):
|
|||
'href': 'http://orig/@ryan/salmon',
|
||||
}],
|
||||
})
|
||||
mock_get.side_effect = [self.reply, self.orig, atom, webfinger]
|
||||
mock_get.side_effect = [self.reply, self.orig, orig_atom, webfinger]
|
||||
|
||||
got = app.get_response('/webmention', method='POST', body=urllib.urlencode({
|
||||
'source': 'http://a/reply',
|
||||
|
|
|
@ -47,13 +47,10 @@ class WebmentionHandler(webapp2.RequestHandler):
|
|||
source_obj = microformats2.json_to_object(entry)
|
||||
logging.info('Converted to AS: %s', json.dumps(source_obj, indent=2))
|
||||
|
||||
return self.send_salmon(source_obj, target_url=target)
|
||||
|
||||
# fetch target page as AS object
|
||||
try:
|
||||
resp = common.requests_get(target, headers=activitypub.CONNEG_HEADER,
|
||||
log=True)
|
||||
target_obj = resp.json()
|
||||
except requests.HTTPError as e:
|
||||
if e.response.status_code // 100 == 4:
|
||||
return self.send_salmon(source_obj, target_url=target)
|
||||
|
@ -62,26 +59,15 @@ class WebmentionHandler(webapp2.RequestHandler):
|
|||
if resp.headers.get('Content-Type').startswith('text/html'):
|
||||
return self.send_salmon(source_obj, target_resp=resp)
|
||||
|
||||
# post-process AS1 to look enough like AS2 to work
|
||||
in_reply_tos = util.get_list(source_obj, 'inReplyTo')
|
||||
if in_reply_tos:
|
||||
source_obj['inReplyTo'] = in_reply_tos[0]['url']
|
||||
if len(in_reply_tos) > 1:
|
||||
logging.warning("AS2 doesn't support multiple inReplyTo URLs! "
|
||||
'Only using the first: %s' % source_obj['inReplyTo'])
|
||||
source_obj.setdefault('cc', []).extend([
|
||||
activitypub.PUBLIC_AUDIENCE,
|
||||
source_obj['inReplyTo'],
|
||||
])
|
||||
|
||||
# find actor's inbox
|
||||
target_obj = resp.json()
|
||||
inbox_url = target_obj.get('inbox')
|
||||
|
||||
if not inbox_url:
|
||||
# fetch actor as AS object
|
||||
actor_url = target_obj.get('actor') or target_obj.get('attributedTo')
|
||||
if isinstance(actor_url, dict):
|
||||
actor_url = actor.get('url')
|
||||
actor_url = actor_url.get('url')
|
||||
if not actor_url:
|
||||
self.abort(400, 'Target object has no actor or attributedTo URL')
|
||||
|
||||
|
@ -95,6 +81,18 @@ class WebmentionHandler(webapp2.RequestHandler):
|
|||
# self.abort(400, 'Target actor has no inbox')
|
||||
return self.send_salmon(source_obj, target_url=target)
|
||||
|
||||
# post-process AS1 to look enough like AS2 to work
|
||||
in_reply_tos = util.get_list(source_obj, 'inReplyTo')
|
||||
if in_reply_tos:
|
||||
source_obj['inReplyTo'] = in_reply_tos[0]['url']
|
||||
if len(in_reply_tos) > 1:
|
||||
logging.warning("AS2 doesn't support multiple inReplyTo URLs! "
|
||||
'Only using the first: %s' % source_obj['inReplyTo'])
|
||||
source_obj.setdefault('cc', []).extend([
|
||||
activitypub.PUBLIC_AUDIENCE,
|
||||
source_obj['inReplyTo'],
|
||||
])
|
||||
|
||||
# deliver source object to target actor's inbox
|
||||
resp = common.requests_post(
|
||||
urlparse.urljoin(target, inbox_url), json=source_obj,
|
||||
|
@ -128,8 +126,9 @@ class WebmentionHandler(webapp2.RequestHandler):
|
|||
# original post's author to make it show up as a reply:
|
||||
# app/services/process_interaction_service.rb
|
||||
# ...so add them as a tag, which atom renders as a rel-mention link.
|
||||
if entry.authors:
|
||||
url = entry.authors[0].href
|
||||
authors = entry.get('authors', None)
|
||||
if authors:
|
||||
url = entry.authors[0].get('href')
|
||||
if url:
|
||||
source_obj.setdefault('tags', []).append({'url': url})
|
||||
|
||||
|
@ -161,8 +160,10 @@ class WebmentionHandler(webapp2.RequestHandler):
|
|||
|
||||
# sign reply and wrap in magic envelope
|
||||
# TODO: use author h-card's u-url?
|
||||
# TODO: person emoji username
|
||||
# BETTER: TODO: extract u-nickname or first name
|
||||
domain = urlparse.urlparse(source_url).netloc.split(':')[0]
|
||||
key = models.MagicKey.get_or_create(domain)
|
||||
key = models.MagicKey.get_or_create('@' + domain)
|
||||
magic_envelope = magicsigs.magic_envelope(
|
||||
entry, common.ATOM_CONTENT_TYPE, key)
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue