add Web.maybe_superfeedr_unsubscribe

for #550
pull/746/head
Ryan Barrett 2023-12-05 08:51:27 -08:00
rodzic b0f278e033
commit d14b1bc04c
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
2 zmienionych plików z 67 dodań i 5 usunięć

Wyświetl plik

@ -1,5 +1,6 @@
"""Unit tests for webmention.py.""" """Unit tests for webmention.py."""
import copy import copy
from datetime import timedelta
from unittest.mock import ANY, patch from unittest.mock import ANY, patch
from flask import g, get_flashed_messages from flask import g, get_flashed_messages
@ -518,9 +519,11 @@ class WebTest(TestCase):
user = Web.get_or_create('user.com') user = Web.get_or_create('user.com')
self.assertEqual('user.com', user.key.id()) self.assertEqual('user.com', user.key.id())
self.assert_entities_equal(user, self.user, self.assert_entities_equal(
ignore=['superfeedr_subscribed', 'updated']) user, self.user,
self.assertTrue(user.superfeedr_subscribed) ignore=['superfeedr_subscribed', 'superfeedr_subscribed_feed', 'updated'])
self.assertEqual(NOW, user.superfeedr_subscribed)
self.assertEqual('https://foo/atom', user.superfeedr_subscribed_feed)
self.assert_req(mock_post, SUPERFEEDR_PUSH_API, data={ self.assert_req(mock_post, SUPERFEEDR_PUSH_API, data={
'hub.mode': 'subscribe', 'hub.mode': 'subscribe',
@ -1813,7 +1816,6 @@ class WebTest(TestCase):
self.assertEqual(400, got.status_code) self.assertEqual(400, got.status_code)
@patch('oauth_dropins.webutil.appengine_config.tasks_client.create_task') @patch('oauth_dropins.webutil.appengine_config.tasks_client.create_task')
@patch('oauth_dropins.webutil.appengine_info.LOCAL_SERVER', False)
def test_maybe_superfeedr_subscribe(self, mock_create_task, mock_get, mock_post): def test_maybe_superfeedr_subscribe(self, mock_create_task, mock_get, mock_post):
common.RUN_TASKS_INLINE = False common.RUN_TASKS_INLINE = False
@ -1862,6 +1864,8 @@ class WebTest(TestCase):
'retrieve': 'true', 'retrieve': 'true',
}, auth=ANY) }, auth=ANY)
self.assertEqual(NOW, self.user.key.get().superfeedr_subscribed) 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') obj = Object.get_by_id('http://domain.tld/entry/1')
self.assertIn(entries[0], obj.atom) self.assertIn(entries[0], obj.atom)
@ -1873,7 +1877,6 @@ class WebTest(TestCase):
self.assert_task(mock_create_task, 'receive', '/queue/receive', self.assert_task(mock_create_task, 'receive', '/queue/receive',
obj=obj.key.urlsafe(), authed_as='user.com') obj=obj.key.urlsafe(), authed_as='user.com')
def test_maybe_superfeedr_subscribe_no_feed(self, mock_get, mock_post): def test_maybe_superfeedr_subscribe_no_feed(self, mock_get, mock_post):
self.user.obj.mf2 = ACTOR_MF2 # no rel-urls self.user.obj.mf2 = ACTOR_MF2 # no rel-urls
web.maybe_superfeedr_subscribe(self.user) web.maybe_superfeedr_subscribe(self.user)
@ -1885,6 +1888,38 @@ class WebTest(TestCase):
web.maybe_superfeedr_subscribe(self.user) web.maybe_superfeedr_subscribe(self.user)
# should be a noop # 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): def _test_verify(self, redirects, hcard, actor, redirects_error=None):
self.user.has_redirects = False self.user.has_redirects = False
self.user.put() self.user.put()

27
web.py
Wyświetl plik

@ -97,6 +97,7 @@ class Web(User, Protocol):
has_hcard = ndb.BooleanProperty() has_hcard = ndb.BooleanProperty()
last_webmention_in = ndb.DateTimeProperty(tzinfo=timezone.utc) last_webmention_in = ndb.DateTimeProperty(tzinfo=timezone.utc)
superfeedr_subscribed = ndb.DateTimeProperty(tzinfo=timezone.utc) superfeedr_subscribed = ndb.DateTimeProperty(tzinfo=timezone.utc)
superfeedr_subscribed_feed = ndb.StringProperty()
# Originally, BF served Web users' AP actor ids on fed.brid.gy, eg # Originally, BF served Web users' AP actor ids on fed.brid.gy, eg
# https://fed.brid.gy/snarfed.org . When we started adding new protocols, we # https://fed.brid.gy/snarfed.org . When we started adding new protocols, we
@ -642,6 +643,7 @@ def maybe_superfeedr_subscribe(user):
resp.raise_for_status() resp.raise_for_status()
user.superfeedr_subscribed = util.now() user.superfeedr_subscribed = util.now()
user.superfeedr_subscribed_feed = url
user.put() user.put()
# handle feed entries (posts) # handle feed entries (posts)
@ -650,6 +652,31 @@ def maybe_superfeedr_subscribe(user):
return _superfeedr_notify(resp.text, user=user) 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()
# generate/check per-user token for auth? # generate/check per-user token for auth?
# or https://documentation.superfeedr.com/subscribers.html#http-authentication ? # or https://documentation.superfeedr.com/subscribers.html#http-authentication ?
@app.post(f'/superfeedr/notify/<regex("{DOMAIN_RE}"):domain>') @app.post(f'/superfeedr/notify/<regex("{DOMAIN_RE}"):domain>')