web: start removing superfeedr code

pull/777/head
Ryan Barrett 2024-01-04 16:32:39 -10:00
rodzic 56b44ac8a5
commit 6025925c26
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
2 zmienionych plików z 1 dodań i 235 usunięć

Wyświetl plik

@ -23,7 +23,7 @@ from common import CONTENT_TYPE_HTML
from flask_app import app
from models import Follower, Object
import web
from web import SUPERFEEDR_PUSH_API, TASKS_LOCATION, Web
from web import TASKS_LOCATION, Web
from . import test_activitypub
from .testutil import Fake, TestCase
@ -1962,129 +1962,6 @@ class WebTest(TestCase):
self.assert_task(mock_create_task, 'poll-feed', '/queue/poll-feed',
domain='user.com', eta_seconds=expected_eta)
def test_superfeedr_notify_no_user(self, *_):
orig_count = Object.query().count()
got = self.post('/webmention', data={'source': 'https://nope.com/post'})
self.assertEqual(400, got.status_code)
self.assertEqual(orig_count, Object.query().count())
def test_superfeedr_notify_no_id(self, *mocks):
got = self.post('/superfeedr/notify/user.com', data="""\
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom">
<content>I hereby post.</content>
</entry>
""", headers={'Content-Type': atom.CONTENT_TYPE})
self.assertEqual(400, got.status_code)
@patch('oauth_dropins.webutil.appengine_config.tasks_client.create_task')
def test_maybe_superfeedr_subscribe(self, mock_create_task, mock_get, mock_post):
common.RUN_TASKS_INLINE = False
entries = ["""\
<author>
<uri>http://my/site</uri>
<name>Mr. Bob</name>
</author>
<id>domain.tld:09/05/03-1</id>
<uri>http://domain.tld/entry/1</uri>
<published>2013-04-21T14:00:40+02:00</published>
<updated>2013-04-21T14:00:40+02:00</updated>
<title>Entry published on hour ago</title>
<content type="text">Entry published on hour ago when it was shiny outside, but now it's raining</content>
<summary type="text">Entry published on hour ago...</summary>
""", """\
<author>
<uri>http://eve/</uri>
</author>
<id>http://domain.tld/entry/2</id>
<title>Second post</title>
<content type="text">Something else</content>
"""]
self.assertIsNone(self.user.superfeedr_subscribed)
self.user.obj.mf2 = ACTOR_MF2_REL_FEED_URL
self.user.has_redirects = False
# https://documentation.superfeedr.com/schema.html#entries
mock_post.return_value = requests_response(f"""\
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<entry>
{entries[0]}
</entry>
<entry>
{entries[1]}
</entry>
</feed>
""", content_type=atom.CONTENT_TYPE)
web.maybe_superfeedr_subscribe(self.user)
self.assert_req(mock_post, SUPERFEEDR_PUSH_API, data={
'hub.mode': 'subscribe',
'hub.topic': 'https://foo/atom',
'hub.callback': 'http://localhost/superfeedr/notify/user.com',
'format': 'atom',
'retrieve': 'true',
}, auth=ANY)
self.assertEqual(NOW, self.user.key.get().superfeedr_subscribed)
self.assertEqual('https://foo/atom',
self.user.key.get().superfeedr_subscribed_feed)
obj = Object.get_by_id('http://domain.tld/entry/1')
self.assertIn(entries[0], obj.atom)
self.assert_task(mock_create_task, 'receive', '/queue/receive',
obj=obj.key.urlsafe(), authed_as='user.com')
obj = Object.get_by_id('http://domain.tld/entry/2')
self.assertIn(entries[1], obj.atom)
self.assert_task(mock_create_task, 'receive', '/queue/receive',
obj=obj.key.urlsafe(), authed_as='user.com')
def test_maybe_superfeedr_subscribe_no_feed(self, mock_get, mock_post):
self.user.obj.mf2 = ACTOR_MF2 # no rel-urls
web.maybe_superfeedr_subscribe(self.user)
self.assertIsNone(self.user.key.get().superfeedr_subscribed)
def test_maybe_superfeedr_subscribe_already_subscribed(self, mock_get, mock_post):
self.user.superfeedr_subscribed = NOW
self.user.put()
web.maybe_superfeedr_subscribe(self.user)
# should be a noop
def test_maybe_superfeedr_unsubscribe(self, mock_get, mock_post):
appengine_info.LOCAL_SERVER = False
self.user.superfeedr_subscribed = NOW
self.user.superfeedr_subscribed_feed = 'http://feed'
self.user.put()
mock_post.return_value = requests_response('OK')
web.maybe_superfeedr_unsubscribe(self.user)
self.assert_req(mock_post, SUPERFEEDR_PUSH_API, data={
'hub.mode': 'unsubscribe',
'hub.topic': 'http://feed',
'hub.callback': 'http://localhost/superfeedr/notify/user.com',
}, auth=ANY)
def test_maybe_superfeedr_unsubscribe_not_subscribed(self, mock_get, mock_post):
appengine_info.LOCAL_SERVER = False
web.maybe_superfeedr_unsubscribe(self.user)
mock_post.assert_not_called()
def test_maybe_superfeedr_unsubscribe_last_webmention_later(self, _, mock_post):
appengine_info.LOCAL_SERVER = False
self.user.superfeedr_subscribed = NOW
self.user.last_webmention_in = NOW + timedelta(days=1)
self.user.put()
web.maybe_superfeedr_unsubscribe(self.user)
mock_post.assert_not_called()
def _test_verify(self, redirects, hcard, actor, redirects_error=None):
self.user.has_redirects = False
self.user.put()

111
web.py
Wyświetl plik

@ -56,14 +56,10 @@ NON_TLDS = frozenset((
'yml',
))
SUPERFEEDR_PUSH_API = 'https://push.superfeedr.com'
SUPERFEEDR_USERNAME = util.read('superfeedr_username')
SUPERFEEDR_TOKEN = util.read('superfeedr_token')
FEED_TYPES = {
atom.CONTENT_TYPE.split(';')[0]: 'atom',
rss.CONTENT_TYPE.split(';')[0]: 'rss',
}
MIN_FEED_POLL_PERIOD = timedelta(hours=2)
MAX_FEED_POLL_PERIOD = timedelta(weeks=1)
@ -626,71 +622,6 @@ def maybe_superfeedr_subscribe(user):
logger.info(f"User {user.key.id()} has no mf2, can't subscribe via Superfeedr")
return
# discover feed
for url, info in user.obj.mf2.get('rel-urls', {}).items():
if ('alternate' in info.get('rels', [])
and info.get('type', '').split(';')[0] in FEED_TYPES.keys()):
break
else:
logger.info(f"User {user.key.id()} has no feed URL, can't subscribe")
return
# subscribe
logger.info(f'Subscribing to {url} via Superfeedr')
if appengine_info.LOCAL_SERVER:
logger.info(f"Skipping since we're local")
return
auth = HTTPBasicAuth(SUPERFEEDR_USERNAME, SUPERFEEDR_TOKEN)
resp = util.requests_post(SUPERFEEDR_PUSH_API, auth=auth, data={
'hub.mode': 'subscribe',
'hub.topic': url,
'hub.callback': common.host_url(f'/superfeedr/notify/{user.key.id()}'),
# TODO
# 'hub.secret': 'xxx',
'format': 'atom',
'retrieve': 'true',
})
try:
resp.raise_for_status()
except BaseException as e:
util.interpret_http_exception(e)
return
user.superfeedr_subscribed = util.now()
user.superfeedr_subscribed_feed = url
user.put()
# handle feed entries (posts)
if (resp.headers.get('Content-Type', '').split(';')[0] ==
atom.CONTENT_TYPE.split(';')[0]):
return _superfeedr_notify(resp.text, user=user)
def maybe_superfeedr_unsubscribe(user):
"""Subscribes to a user's Atom or RSS feed in Superfeedr.
Args:
user (Web)
"""
if (not user.superfeedr_subscribed
or not user.superfeedr_subscribed_feed
or (user.last_webmention_in
and user.last_webmention_in > user.superfeedr_subscribed)
or appengine_info.LOCAL_SERVER):
return
# unsubscribe
logger.info(f'Unsubscribing from Superfeedr for {user.superfeedr_subscribed_feed}')
auth = HTTPBasicAuth(SUPERFEEDR_USERNAME, SUPERFEEDR_TOKEN)
resp = util.requests_post(SUPERFEEDR_PUSH_API, auth=auth, data={
'hub.mode': 'unsubscribe',
'hub.topic': user.superfeedr_subscribed_feed,
'hub.callback': common.host_url(f'/superfeedr/notify/{user.key.id()}'),
})
resp.raise_for_status()
@app.post(f'/queue/poll-feed')
def poll_feed_task():
@ -765,48 +696,6 @@ def poll_feed_task():
return 'OK'
# generate/check per-user token for auth?
# or https://documentation.superfeedr.com/subscribers.html#http-authentication ?
@app.post(f'/superfeedr/notify/<regex("{DOMAIN_RE}"):domain>')
def superfeedr_notify(domain):
"""Superfeedr notification handler.
https://documentation.superfeedr.com/publishers.html#subscription-callback
"""
logger.info(f'Got:\n{request.get_data(as_text=True)}')
type = request.headers.get('Content-Type', '').split(';')[0]
if type != atom.CONTENT_TYPE.split(';')[0]:
error(f'Expected Content-Type {atom.CONTENT_TYPE}, got {type}', status=406)
user = Web.get_by_id(domain)
if not user:
error(f'No user found for domain {domain}', status=304)
return _superfeedr_notify(request.get_data(as_text=True), user=user)
def _superfeedr_notify(doc, user):
"""Creates :class:`Object`s and ``receive`` tasks for an Atom feed or entry.
Args:
doc (str): Atom document
user (Web): user this feed came from
"""
for entry in atom.extract_entries(doc):
obj_as1 = Object(atom=entry).as1
logger.info(f'Converted to AS1: {json_dumps(obj_as1, indent=2)}')
id = obj_as1.get('id')
if not id:
return error('No id or URL!')
obj = Object.get_or_create(id=id, atom=entry, source_protocol=Web.ABBREV)
common.create_task(queue='receive', obj=obj.key.urlsafe(),
authed_as=user.key.id())
return 'OK'
@app.post('/queue/webmention')
@cloud_tasks_only
def webmention_task():