Skip to content

Commit

Permalink
web: start removing superfeedr code
Browse files Browse the repository at this point in the history
  • Loading branch information
snarfed committed Jan 5, 2024
1 parent 56b44ac commit 6025925
Show file tree
Hide file tree
Showing 2 changed files with 1 addition and 235 deletions.
125 changes: 1 addition & 124 deletions tests/test_web.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
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

Expand Down Expand Up @@ -1962,129 +1962,6 @@ def test_poll_feed_rss(self, mock_create_task, mock_get, _):
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()
Expand Down
111 changes: 0 additions & 111 deletions web.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,10 @@
'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)

Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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():
Expand Down

0 comments on commit 6025925

Please sign in to comment.