kopia lustrzana https://github.com/snarfed/bridgy-fed
rodzic
023f2aa536
commit
4e0fb6536c
|
@ -19,6 +19,13 @@ indexes:
|
||||||
- name: updated
|
- name: updated
|
||||||
direction: desc
|
direction: desc
|
||||||
|
|
||||||
|
- kind: Response
|
||||||
|
properties:
|
||||||
|
- name: direction
|
||||||
|
- name: domain
|
||||||
|
- name: created
|
||||||
|
direction: desc
|
||||||
|
|
||||||
- kind: Follower
|
- kind: Follower
|
||||||
properties:
|
properties:
|
||||||
- name: dest
|
- name: dest
|
||||||
|
|
39
pages.py
39
pages.py
|
@ -7,6 +7,7 @@ import urllib.parse
|
||||||
|
|
||||||
from flask import redirect, render_template, request
|
from flask import redirect, render_template, request
|
||||||
from google.cloud.ndb.stats import KindStat
|
from google.cloud.ndb.stats import KindStat
|
||||||
|
from granary import as2, atom, microformats2, rss
|
||||||
from oauth_dropins.webutil import flask_util, logs, util
|
from oauth_dropins.webutil import flask_util, logs, util
|
||||||
from oauth_dropins.webutil.flask_util import error
|
from oauth_dropins.webutil.flask_util import error
|
||||||
from oauth_dropins.webutil.util import json_dumps, json_loads
|
from oauth_dropins.webutil.util import json_dumps, json_loads
|
||||||
|
@ -108,6 +109,44 @@ def following(domain):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get(f'/user/<regex("{common.DOMAIN_RE}"):domain>/feed')
|
||||||
|
def feed(domain):
|
||||||
|
format = request.args.get('format', 'html')
|
||||||
|
if format not in ('html', 'atom', 'rss'):
|
||||||
|
error(f'format {format} not supported; expected html, atom, or rss')
|
||||||
|
|
||||||
|
as2_activities, _, _ = Activity.query(
|
||||||
|
Activity.domain == domain, Activity.direction == 'in'
|
||||||
|
).order(-Activity.created
|
||||||
|
).fetch_page(PAGE_SIZE)
|
||||||
|
as1_activities = [as2.to_as1(json_loads(a.source_as2))
|
||||||
|
for a in as2_activities
|
||||||
|
if a.source_as2]
|
||||||
|
as1_activities = [a for a in as1_activities
|
||||||
|
if a.get('verb') not in ('like', 'update', 'follow')]
|
||||||
|
|
||||||
|
actor = {
|
||||||
|
'displayName': domain,
|
||||||
|
'url': f'https://{domain}',
|
||||||
|
}
|
||||||
|
title = f'Bridgy Fed feed for {domain}'
|
||||||
|
extra = '<link rel="stylesheet" href="/static/feed.css" type="text/css" />'
|
||||||
|
if not as1_activities:
|
||||||
|
extra += '\n<p>Nothing yet. Follow more people, check back soon!</p>'
|
||||||
|
|
||||||
|
if format == 'html':
|
||||||
|
return microformats2.activities_to_html(as1_activities, extra=extra,
|
||||||
|
body_class='h-feed')
|
||||||
|
elif format == 'atom':
|
||||||
|
body = atom.activities_to_atom(as1_activities, actor=actor, title=title,
|
||||||
|
request_url=request.url)
|
||||||
|
return body, {'Content-Type': atom.CONTENT_TYPE}
|
||||||
|
elif format == 'rss':
|
||||||
|
body = rss.from_activities(as1_activities, actor=actor, title=title,
|
||||||
|
feed_url=request.url)
|
||||||
|
return body, {'Content-Type': rss.CONTENT_TYPE}
|
||||||
|
|
||||||
|
|
||||||
@app.get('/responses') # deprecated
|
@app.get('/responses') # deprecated
|
||||||
def recent_deprecated():
|
def recent_deprecated():
|
||||||
return redirect('/recent', code=301)
|
return redirect('/recent', code=301)
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/** From Bridgy's mf2 handlers:
|
||||||
|
* https://github.com/snarfed/bridgy/blob/0b4b37cab61257510b45aee5b0678ba53af69d80/handlers.py#L43-L70
|
||||||
|
*
|
||||||
|
* Also see:
|
||||||
|
* https://github.com/kevinmarks/unmung/blob/master/styles/hfeed.css
|
||||||
|
* https://github.com/kevinmarks/unmung/blob/master/styles/mastoview.css
|
||||||
|
*/
|
||||||
|
body {
|
||||||
|
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-uid {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.u-photo {
|
||||||
|
max-width: 50px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.e-content {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
|
@ -11,6 +11,9 @@
|
||||||
<h3 style="display: inline">
|
<h3 style="display: inline">
|
||||||
| <a href="/user/{{ domain }}/followers">{{ followers }} follower{% if followers != '1' %}s{% endif %}</a>
|
| <a href="/user/{{ domain }}/followers">{{ followers }} follower{% if followers != '1' %}s{% endif %}</a>
|
||||||
| <a href="/user/{{ domain }}/following">following {{ following }}</a>
|
| <a href="/user/{{ domain }}/following">following {{ following }}</a>
|
||||||
|
| <a href="/user/{{ domain }}/feed">HTML</a>,
|
||||||
|
<a href="/user/{{ domain }}/feed?format=atom">Atom</a>,
|
||||||
|
<a href="/user/{{ domain }}/feed?format=rss">RSS</a> feeds
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
"""Unit tests for pages.py."""
|
||||||
|
from oauth_dropins.webutil import util
|
||||||
|
from oauth_dropins.webutil.util import json_dumps, json_loads
|
||||||
|
from granary import as2, atom, microformats2, rss
|
||||||
|
|
||||||
|
from models import Follower, Activity
|
||||||
|
from . import testutil
|
||||||
|
from .test_activitypub import LIKE, MENTION, NOTE, REPLY
|
||||||
|
|
||||||
|
|
||||||
|
def contents(activities):
|
||||||
|
return [a['object']['content'] for a in activities]
|
||||||
|
|
||||||
|
|
||||||
|
class PagesTest(testutil.TestCase):
|
||||||
|
|
||||||
|
EXPECTED = contents([as2.to_as1(REPLY), as2.to_as1(NOTE)])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_activities():
|
||||||
|
Activity(id='a', domain=['foo.com'], direction='in',
|
||||||
|
source_as2=json_dumps(NOTE)).put()
|
||||||
|
# different domain
|
||||||
|
Activity(id='b', domain=['bar.org'], direction='in',
|
||||||
|
source_as2=json_dumps(MENTION)).put()
|
||||||
|
# empty, should be skipped
|
||||||
|
Activity(id='c', domain=['foo.com'], direction='in').put()
|
||||||
|
Activity(id='d', domain=['foo.com'], direction='in',
|
||||||
|
source_as2=json_dumps(REPLY)).put()
|
||||||
|
# wrong direction
|
||||||
|
Activity(id='e', domain=['foo.com'], direction='out',
|
||||||
|
source_as2=json_dumps(NOTE)).put()
|
||||||
|
# skip Likes
|
||||||
|
Activity(id='f', domain=['foo.com'], direction='in',
|
||||||
|
source_as2=json_dumps(LIKE)).put()
|
||||||
|
|
||||||
|
def test_feed_html_empty(self):
|
||||||
|
got = self.client.get('/user/foo.com/feed')
|
||||||
|
self.assert_equals(200, got.status_code)
|
||||||
|
self.assert_equals([], microformats2.html_to_activities(got.text))
|
||||||
|
|
||||||
|
def test_feed_html(self):
|
||||||
|
self.add_activities()
|
||||||
|
got = self.client.get('/user/foo.com/feed')
|
||||||
|
self.assert_equals(200, got.status_code)
|
||||||
|
self.assert_equals(self.EXPECTED,
|
||||||
|
contents(microformats2.html_to_activities(got.text)))
|
||||||
|
|
||||||
|
def test_feed_atom_empty(self):
|
||||||
|
got = self.client.get('/user/foo.com/feed?format=atom')
|
||||||
|
self.assert_equals(200, got.status_code)
|
||||||
|
self.assert_equals([], atom.atom_to_activities(got.text))
|
||||||
|
|
||||||
|
def test_feed_atom(self):
|
||||||
|
self.add_activities()
|
||||||
|
got = self.client.get('/user/foo.com/feed?format=atom')
|
||||||
|
self.assert_equals(200, got.status_code)
|
||||||
|
self.assert_equals(self.EXPECTED, contents(atom.atom_to_activities(got.text)))
|
||||||
|
|
||||||
|
def test_feed_rss_empty(self):
|
||||||
|
got = self.client.get('/user/foo.com/feed?format=rss')
|
||||||
|
self.assert_equals(200, got.status_code)
|
||||||
|
self.assert_equals([], rss.to_activities(got.text))
|
||||||
|
|
||||||
|
def test_feed_rss(self):
|
||||||
|
self.add_activities()
|
||||||
|
got = self.client.get('/user/foo.com/feed?format=rss')
|
||||||
|
self.assert_equals(200, got.status_code)
|
||||||
|
self.assert_equals(self.EXPECTED, contents(rss.to_activities(got.text)))
|
|
@ -43,7 +43,7 @@ class RenderTest(testutil.TestCase):
|
||||||
<html>
|
<html>
|
||||||
<head><meta charset="utf-8">
|
<head><meta charset="utf-8">
|
||||||
<meta http-equiv="refresh" content="0;url=abc"></head>
|
<meta http-equiv="refresh" content="0;url=abc"></head>
|
||||||
<body>
|
<body class="">
|
||||||
<article class="h-entry">
|
<article class="h-entry">
|
||||||
<span class="p-uid">http://this/reply</span>
|
<span class="p-uid">http://this/reply</span>
|
||||||
<a class="u-url" href="http://this/reply">http://this/reply</a>
|
<a class="u-url" href="http://this/reply">http://this/reply</a>
|
||||||
|
|
Ładowanie…
Reference in New Issue